From cf13177e99d20e8db632ad6ed5defd3818f7d901 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Mitsuharu Date: Thu, 4 Jul 2013 19:25:54 +0900 Subject: [PATCH] Add multi-monitor support on W32. --- etc/NEWS | 8 ++ lisp/ChangeLog | 8 ++ lisp/frame.el | 28 ++++- src/ChangeLog | 24 +++++ src/w32fns.c | 281 +++++++++++++++++++++++++++++++++++++++++++++---- src/w32term.c | 27 +++-- 6 files changed, 347 insertions(+), 29 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 7f217facff..9c76c2e016 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -125,6 +125,14 @@ used to conveniently prepend/append new face attributes to text. `frame-monitor-attributes' can be used to obtain information about each physical monitor on multi-monitor setups. +*** The functions `display-pixel-width' and `display-pixel-height' now +behave consistently among the platforms: they return the pixel width +or height for all physical monitors associated with the given display +as just they were on X11. To get information for each physical +monitor, use the new functions above. Similar notes also apply to +`x-display-pixel-width', `x-display-pixel-height', `display-mm-width', +`display-mm-height', `x-display-mm-width', and `x-display-mm-height'. + * Editing Changes in Emacs 24.4 diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 7921f77ca0..7bdd21bc3e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,11 @@ +2013-07-04 YAMAMOTO Mitsuharu + + * frame.el (display-pixel-height, display-pixel-width) + (display-mm-height, display-mm-width): Mention behavior on + multi-monitor setups in docstrings. + (w32-display-monitor-attributes-list): Declare function. + (display-monitor-attributes-list): Use it. + 2013-07-04 Michael Albinus * filenotify.el: New package. diff --git a/lisp/frame.el b/lisp/frame.el index 0f8fc523a1..3ac24a509a 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -1365,7 +1365,11 @@ frame's display)." (defun display-pixel-height (&optional display) "Return the height of DISPLAY's screen in pixels. -For character terminals, each character counts as a single pixel." +For character terminals, each character counts as a single pixel. +For graphical terminals, note that on \"multi-monitor\" setups this +refers to the pixel height for all physical monitors associated +with DISPLAY. To get information for each physical monitor, use +`display-monitor-attributes-list'." (let ((frame-type (framep-on-display display))) (cond ((memq frame-type '(x w32 ns)) @@ -1377,7 +1381,11 @@ For character terminals, each character counts as a single pixel." (defun display-pixel-width (&optional display) "Return the width of DISPLAY's screen in pixels. -For character terminals, each character counts as a single pixel." +For character terminals, each character counts as a single pixel. +For graphical terminals, note that on \"multi-monitor\" setups this +refers to the pixel width for all physical monitors associated +with DISPLAY. To get information for each physical monitor, use +`display-monitor-attributes-list'." (let ((frame-type (framep-on-display display))) (cond ((memq frame-type '(x w32 ns)) @@ -1408,7 +1416,11 @@ displays not explicitly specified." (defun display-mm-height (&optional display) "Return the height of DISPLAY's screen in millimeters. System values can be overridden by `display-mm-dimensions-alist'. -If the information is unavailable, value is nil." +If the information is unavailable, value is nil. +For graphical terminals, note that on \"multi-monitor\" setups this +refers to the height in millimeters for all physical monitors +associated with DISPLAY. To get information for each physical +monitor, use `display-monitor-attributes-list'." (and (memq (framep-on-display display) '(x w32 ns)) (or (cddr (assoc (or display (frame-parameter nil 'display)) display-mm-dimensions-alist)) @@ -1420,7 +1432,11 @@ If the information is unavailable, value is nil." (defun display-mm-width (&optional display) "Return the width of DISPLAY's screen in millimeters. System values can be overridden by `display-mm-dimensions-alist'. -If the information is unavailable, value is nil." +If the information is unavailable, value is nil. +For graphical terminals, note that on \"multi-monitor\" setups this +refers to the width in millimeters for all physical monitors +associated with DISPLAY. To get information for each physical +monitor, use `display-monitor-attributes-list'." (and (memq (framep-on-display display) '(x w32 ns)) (or (cadr (assoc (or display (frame-parameter nil 'display)) display-mm-dimensions-alist)) @@ -1495,6 +1511,8 @@ The value is one of the symbols `static-gray', `gray-scale', (declare-function x-display-monitor-attributes-list "xfns.c" (&optional terminal)) +(declare-function w32-display-monitor-attributes-list "w32fns.c" + (&optional display)) (declare-function ns-display-monitor-attributes-list "nsfns.m" (&optional terminal)) @@ -1530,6 +1548,8 @@ monitors." (cond ((eq frame-type 'x) (x-display-monitor-attributes-list display)) + ((eq frame-type 'w32) + (w32-display-monitor-attributes-list display)) ((eq frame-type 'ns) (ns-display-monitor-attributes-list display)) (t diff --git a/src/ChangeLog b/src/ChangeLog index 416e60f30a..9012f5ba16 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,27 @@ +2013-07-04 YAMAMOTO Mitsuharu + + * w32fns.c (Qgeometry, Qworkarea, Qmm_size, Qframes): New variables. + (syms_of_w32fns): DEFSYM them. + (MONITORINFOF_PRIMARY, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN) + (CCHDEVICENAME): Define macros if not defined. + (struct MONITOR_INFO_EX): New struct. + (MonitorEnum_Proc, EnumDisplayMonitors_Proc): New prototypes. + (enum_display_monitors_fn): New variable. + (globals_of_w32fns): Initialize it. + (Fx_display_pixel_width, Fx_display_pixel_height) + (Fx_display_mm_height, Fx_display_mm_width): Mention behavior on + multi-monitor setups in docstrings. + (Fx_display_mm_height, Fx_display_mm_width): Approximate whole + screen size by primary monitor's millimeter per pixel. + (w32_monitor_enum, w32_display_monitor_attributes_list) + (w32_display_monitor_attributes_list_fallback) + (Fw32_display_monitor_attributes_list): New functions. + (syms_of_w32fns): Defsubr Sw32_display_monitor_attributes_list. + + * w32term.c (SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN): Define macros + if not defined. + (x_display_pixel_height, x_display_pixel_width): Use GetSystemMetrics. + 2013-07-04 Michael Albinus * fileio.c (Qfile_notify_error): New error symbol. diff --git a/src/w32fns.c b/src/w32fns.c index 46fb02d96a..3fa23c166e 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -106,6 +106,7 @@ Lisp_Object Qalt; Lisp_Object Qctrl; Lisp_Object Qcontrol; Lisp_Object Qshift; +static Lisp_Object Qgeometry, Qworkarea, Qmm_size, Qframes; /* Prefix for system colors. */ @@ -131,6 +132,15 @@ static HWND track_mouse_window; #ifndef MONITOR_DEFAULT_TO_NEAREST #define MONITOR_DEFAULT_TO_NEAREST 2 #endif +#ifndef MONITORINFOF_PRIMARY +#define MONITORINFOF_PRIMARY 1 +#endif +#ifndef SM_XVIRTUALSCREEN +#define SM_XVIRTUALSCREEN 76 +#endif +#ifndef SM_YVIRTUALSCREEN +#define SM_YVIRTUALSCREEN 77 +#endif /* MinGW headers define MONITORINFO unconditionally, but MSVC ones don't. To avoid a compile error on one or the other, redefine with a new name. */ struct MONITOR_INFO @@ -141,6 +151,18 @@ struct MONITOR_INFO DWORD dwFlags; }; +#ifndef CCHDEVICENAME +#define CCHDEVICENAME 32 +#endif +struct MONITOR_INFO_EX +{ + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + char szDevice[CCHDEVICENAME]; +}; + /* Reportedly, MSVC does not have this in its headers. */ #if defined (_MSC_VER) && _WIN32_WINNT < 0x0500 DECLARE_HANDLE(HMONITOR); @@ -159,6 +181,10 @@ typedef BOOL (WINAPI * GetMonitorInfo_Proc) (IN HMONITOR monitor, OUT struct MONITOR_INFO* info); typedef HMONITOR (WINAPI * MonitorFromWindow_Proc) (IN HWND hwnd, IN DWORD dwFlags); +typedef BOOL CALLBACK (* MonitorEnum_Proc) + (IN HMONITOR monitor, IN HDC hdc, IN RECT *rcMonitor, IN LPARAM dwData); +typedef BOOL (WINAPI * EnumDisplayMonitors_Proc) + (IN HDC hdc, IN RECT *rcClip, IN MonitorEnum_Proc fnEnum, IN LPARAM dwData); TrackMouseEvent_Proc track_mouse_event_fn = NULL; ImmGetCompositionString_Proc get_composition_string_fn = NULL; @@ -168,6 +194,7 @@ ImmSetCompositionWindow_Proc set_ime_composition_window_fn = NULL; MonitorFromPoint_Proc monitor_from_point_fn = NULL; GetMonitorInfo_Proc get_monitor_info_fn = NULL; MonitorFromWindow_Proc monitor_from_window_fn = NULL; +EnumDisplayMonitors_Proc enum_display_monitors_fn = NULL; #ifdef NTGUI_UNICODE #define unicode_append_menu AppendMenuW @@ -4674,7 +4701,11 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width, doc: /* Return the width in pixels of DISPLAY. The optional argument DISPLAY specifies which display to ask about. DISPLAY should be either a frame or a display name (a string). -If omitted or nil, that stands for the selected frame's display. */) +If omitted or nil, that stands for the selected frame's display. + +On \"multi-monitor\" setups this refers to the pixel width for all +physical monitors associated with DISPLAY. To get information for +each physical monitor, use `display-monitor-attributes-list'. */) (Lisp_Object display) { struct w32_display_info *dpyinfo = check_x_display_info (display); @@ -4687,7 +4718,11 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height, doc: /* Return the height in pixels of DISPLAY. The optional argument DISPLAY specifies which display to ask about. DISPLAY should be either a frame or a display name (a string). -If omitted or nil, that stands for the selected frame's display. */) +If omitted or nil, that stands for the selected frame's display. + +On \"multi-monitor\" setups this refers to the pixel height for all +physical monitors associated with DISPLAY. To get information for +each physical monitor, use `display-monitor-attributes-list'. */) (Lisp_Object display) { struct w32_display_info *dpyinfo = check_x_display_info (display); @@ -4779,41 +4814,46 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, doc: /* Return the height in millimeters of DISPLAY. The optional argument DISPLAY specifies which display to ask about. DISPLAY should be either a frame or a display name (a string). -If omitted or nil, that stands for the selected frame's display. */) +If omitted or nil, that stands for the selected frame's display. + +On \"multi-monitor\" setups this refers to the height in millimeters for +all physical monitors associated with DISPLAY. To get information +for each physical monitor, use `display-monitor-attributes-list'. */) (Lisp_Object display) { struct w32_display_info *dpyinfo = check_x_display_info (display); HDC hdc; - int cap; - - hdc = GetDC (dpyinfo->root_window); + double mm_per_pixel; - cap = GetDeviceCaps (hdc, VERTSIZE); + hdc = GetDC (NULL); + mm_per_pixel = ((double) GetDeviceCaps (hdc, VERTSIZE) + / GetDeviceCaps (hdc, VERTRES)); + ReleaseDC (NULL, hdc); - ReleaseDC (dpyinfo->root_window, hdc); - - return make_number (cap); + return make_number (x_display_pixel_height (dpyinfo) * mm_per_pixel + 0.5); } DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, doc: /* Return the width in millimeters of DISPLAY. The optional argument DISPLAY specifies which display to ask about. DISPLAY should be either a frame or a display name (a string). -If omitted or nil, that stands for the selected frame's display. */) +If omitted or nil, that stands for the selected frame's display. + +On \"multi-monitor\" setups this refers to the width in millimeters for +all physical monitors associated with TERMINAL. To get information +for each physical monitor, use `display-monitor-attributes-list'. */) (Lisp_Object display) { struct w32_display_info *dpyinfo = check_x_display_info (display); - HDC hdc; - int cap; + double mm_per_pixel; - hdc = GetDC (dpyinfo->root_window); + hdc = GetDC (NULL); + mm_per_pixel = ((double) GetDeviceCaps (hdc, HORZSIZE) + / GetDeviceCaps (hdc, HORZRES)); + ReleaseDC (NULL, hdc); - cap = GetDeviceCaps (hdc, HORZSIZE); - - ReleaseDC (dpyinfo->root_window, hdc); - - return make_number (cap); + return make_number (x_display_pixel_width (dpyinfo) * mm_per_pixel + 0.5); } DEFUN ("x-display-backing-store", Fx_display_backing_store, @@ -4865,6 +4905,202 @@ If omitted or nil, that stands for the selected frame's display. */) return Qnil; } +static BOOL CALLBACK +w32_monitor_enum (HMONITOR monitor, HDC hdc, RECT *rcMonitor, LPARAM dwData) +{ + Lisp_Object *monitor_list = (Lisp_Object *) dwData; + + *monitor_list = Fcons (make_save_pointer (monitor), *monitor_list); + + return TRUE; +} + +static Lisp_Object +w32_display_monitor_attributes_list (void) +{ + Lisp_Object attributes_list = Qnil, primary_monitor_attributes = Qnil; + Lisp_Object monitor_list = Qnil, monitor_frames, rest, frame; + int i, n_monitors; + HMONITOR *monitors; + struct gcpro gcpro1, gcpro2, gcpro3; + + if (!(enum_display_monitors_fn && get_monitor_info_fn + && monitor_from_window_fn)) + return Qnil; + + if (!enum_display_monitors_fn (NULL, NULL, w32_monitor_enum, + (LPARAM) &monitor_list) + || NILP (monitor_list)) + return Qnil; + + n_monitors = 0; + for (rest = monitor_list; CONSP (rest); rest = XCDR (rest)) + n_monitors++; + + monitors = xmalloc (n_monitors * sizeof (*monitors)); + for (i = 0; i < n_monitors; i++) + { + monitors[i] = XSAVE_POINTER (XCAR (monitor_list), 0); + monitor_list = XCDR (monitor_list); + } + + monitor_frames = Fmake_vector (make_number (n_monitors), Qnil); + FOR_EACH_FRAME (rest, frame) + { + struct frame *f = XFRAME (frame); + + if (FRAME_W32_P (f) && !EQ (frame, tip_frame)) + { + HMONITOR monitor = + monitor_from_window_fn (FRAME_W32_WINDOW (f), + MONITOR_DEFAULT_TO_NEAREST); + + for (i = 0; i < n_monitors; i++) + if (monitors[i] == monitor) + break; + + if (i < n_monitors) + ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i))); + } + } + + GCPRO3 (attributes_list, primary_monitor_attributes, monitor_frames); + + for (i = 0; i < n_monitors; i++) + { + Lisp_Object geometry, workarea, name, attributes = Qnil; + HDC hdc; + int width_mm, height_mm; + struct MONITOR_INFO_EX mi; + + mi.cbSize = sizeof (mi); + if (!get_monitor_info_fn (monitors[i], (struct MONITOR_INFO *) &mi)) + continue; + + hdc = CreateDCA ("DISPLAY", mi.szDevice, NULL, NULL); + if (hdc == NULL) + continue; + width_mm = GetDeviceCaps (hdc, HORZSIZE); + height_mm = GetDeviceCaps (hdc, VERTSIZE); + DeleteDC (hdc); + + attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)), + attributes); + + name = DECODE_SYSTEM (make_unibyte_string (mi.szDevice, + strlen (mi.szDevice))); + attributes = Fcons (Fcons (Qname, name), attributes); + + attributes = Fcons (Fcons (Qmm_size, list2i (width_mm, height_mm)), + attributes); + + workarea = list4i (mi.rcWork.left, mi.rcWork.top, + mi.rcWork.right - mi.rcWork.left, + mi.rcWork.bottom - mi.rcWork.top); + attributes = Fcons (Fcons (Qworkarea, workarea), attributes); + + geometry = list4i (mi.rcMonitor.left, mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top); + attributes = Fcons (Fcons (Qgeometry, geometry), attributes); + + if (mi.dwFlags & MONITORINFOF_PRIMARY) + primary_monitor_attributes = attributes; + else + attributes_list = Fcons (attributes, attributes_list); + } + + if (!NILP (primary_monitor_attributes)) + attributes_list = Fcons (primary_monitor_attributes, attributes_list); + + UNGCPRO; + + xfree (monitors); + + return attributes_list; +} + +static Lisp_Object +w32_display_monitor_attributes_list_fallback (struct w32_display_info *dpyinfo) +{ + Lisp_Object geometry, workarea, frames, rest, frame, attributes = Qnil; + HDC hdc; + double mm_per_pixel; + int pixel_width, pixel_height, width_mm, height_mm; + RECT workarea_rect; + + /* Fallback: treat (possibly) multiple physical monitors as if they + formed a single monitor as a whole. This should provide a + consistent result at least on single monitor environments. */ + attributes = Fcons (Fcons (Qname, build_string ("combined screen")), + attributes); + + frames = Qnil; + FOR_EACH_FRAME (rest, frame) + { + struct frame *f = XFRAME (frame); + + if (FRAME_W32_P (f) && !EQ (frame, tip_frame)) + frames = Fcons (frame, frames); + } + attributes = Fcons (Fcons (Qframes, frames), attributes); + + pixel_width = x_display_pixel_width (dpyinfo); + pixel_height = x_display_pixel_height (dpyinfo); + + hdc = GetDC (NULL); + mm_per_pixel = ((double) GetDeviceCaps (hdc, HORZSIZE) + / GetDeviceCaps (hdc, HORZRES)); + width_mm = pixel_width * mm_per_pixel + 0.5; + mm_per_pixel = ((double) GetDeviceCaps (hdc, VERTSIZE) + / GetDeviceCaps (hdc, VERTRES)); + height_mm = pixel_height * mm_per_pixel + 0.5; + ReleaseDC (NULL, hdc); + attributes = Fcons (Fcons (Qmm_size, list2i (width_mm, height_mm)), + attributes); + + /* GetSystemMetrics below may return 0 for Windows 95 or NT 4.0, but + we don't care. */ + geometry = list4i (GetSystemMetrics (SM_XVIRTUALSCREEN), + GetSystemMetrics (SM_YVIRTUALSCREEN), + pixel_width, pixel_height); + if (SystemParametersInfo (SPI_GETWORKAREA, 0, &workarea_rect, 0)) + workarea = list4i (workarea_rect.left, workarea_rect.top, + workarea_rect.right - workarea_rect.left, + workarea_rect.bottom - workarea_rect.top); + else + workarea = geometry; + attributes = Fcons (Fcons (Qworkarea, workarea), attributes); + + attributes = Fcons (Fcons (Qgeometry, geometry), attributes); + + return list1 (attributes); +} + +DEFUN ("w32-display-monitor-attributes-list", Fw32_display_monitor_attributes_list, + Sw32_display_monitor_attributes_list, + 0, 1, 0, + doc: /* Return a list of physical monitor attributes on the W32 display DISPLAY. + +The optional argument DISPLAY specifies which display to ask about. +DISPLAY should be either a frame or a display name (a string). +If omitted or nil, that stands for the selected frame's display. + +Internal use only, use `display-monitor-attributes-list' instead. */) + (Lisp_Object display) +{ + struct w32_display_info *dpyinfo = check_x_display_info (display); + Lisp_Object attributes_list; + + block_input (); + attributes_list = w32_display_monitor_attributes_list (); + if (NILP (attributes_list)) + attributes_list = w32_display_monitor_attributes_list_fallback (dpyinfo); + unblock_input (); + + return attributes_list; +} + DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0, doc: /* Set the sound generated when the bell is rung. SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent @@ -7357,6 +7593,10 @@ syms_of_w32fns (void) DEFSYM (Qcontrol, "control"); DEFSYM (Qshift, "shift"); DEFSYM (Qfont_param, "font-parameter"); + DEFSYM (Qgeometry, "geometry"); + DEFSYM (Qworkarea, "workarea"); + DEFSYM (Qmm_size, "mm-size"); + DEFSYM (Qframes, "frames"); /* This is the end of symbol initialization. */ @@ -7646,6 +7886,7 @@ only be necessary if the default setting causes problems. */); defsubr (&Sw32_define_rgb_color); defsubr (&Sw32_default_color_map); + defsubr (&Sw32_display_monitor_attributes_list); defsubr (&Sw32_send_sys_command); defsubr (&Sw32_shell_execute); defsubr (&Sw32_register_hot_key); @@ -7707,6 +7948,8 @@ globals_of_w32fns (void) GetProcAddress (user32_lib, "GetMonitorInfoA"); monitor_from_window_fn = (MonitorFromWindow_Proc) GetProcAddress (user32_lib, "MonitorFromWindow"); + enum_display_monitors_fn = (EnumDisplayMonitors_Proc) + GetProcAddress (user32_lib, "EnumDisplayMonitors"); { HMODULE imm32_lib = GetModuleHandle ("imm32.dll"); diff --git a/src/w32term.c b/src/w32term.c index d3174c65bf..fcd5886d5c 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -143,6 +143,15 @@ BOOL (WINAPI *pfnSetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); #define WS_EX_LAYERED 0x80000 #endif +/* SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN are not defined on 95 and + NT4. */ +#ifndef SM_CXVIRTUALSCREEN +#define SM_CXVIRTUALSCREEN 78 +#endif +#ifndef SM_CYVIRTUALSCREEN +#define SM_CYVIRTUALSCREEN 79 +#endif + /* This is a frame waiting to be autoraised, within w32_read_socket. */ struct frame *pending_autoraise_frame; @@ -519,18 +528,24 @@ x_set_frame_alpha (struct frame *f) int x_display_pixel_height (struct w32_display_info *dpyinfo) { - HDC dc = GetDC (NULL); - int pixels = GetDeviceCaps (dc, VERTRES); - ReleaseDC (NULL, dc); + int pixels = GetSystemMetrics (SM_CYVIRTUALSCREEN); + + if (pixels == 0) + /* Fallback for Windows 95 or NT 4.0. */ + pixels = GetSystemMetrics (SM_CYSCREEN); + return pixels; } int x_display_pixel_width (struct w32_display_info *dpyinfo) { - HDC dc = GetDC (NULL); - int pixels = GetDeviceCaps (dc, HORZRES); - ReleaseDC (NULL, dc); + int pixels = GetSystemMetrics (SM_CXVIRTUALSCREEN); + + if (pixels == 0) + /* Fallback for Windows 95 or NT 4.0. */ + pixels = GetSystemMetrics (SM_CXSCREEN); + return pixels; } -- 2.20.1