gtk2/gdk/win32/gdkdisplay-win32.c
Tor Lillqvist 0905c73a88 Cache the display name. There is only one GdkDisplay on Win32, and
2005-09-09  Tor Lillqvist  <tml@novell.com>

	* gdk/win32/gdkdisplay-win32.c (gdk_display_get_name): Cache the
	display name. There is only one GdkDisplay on Win32, and
	constructing the display name isn't entirely trivial, so cacheing
	is probably worth it. For instance GIMP calls this function a lot.
	(gdk_display_open): Call gdk_display_get_name() to prime the
	cached name.
	(gdk_display_get_n_screens, gdk_display_get_screen,
	gdk_display_get_default_screen): Verify parameter correctness like
	the X11 backend does.

	* gdk/win32/gdkscreen-win32.c (gdk_screen_make_display_name):
	Return a freshly allocated string, as the API specifies. Fixes a
	heap corruption problem that caused random errors and crashes in
	GIMP, for instance.
2005-09-08 22:45:40 +00:00

491 lines
13 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 2002,2005 Hans Breuer
* Copyright (C) 2003 Tor Lillqvist
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include "gdk.h"
#include "gdkprivate-win32.h"
#define HAVE_MONITOR_INFO
#if defined(_MSC_VER) && (WINVER < 0x500) && (WINVER > 0x0400)
#include <multimon.h>
#elif defined(_MSC_VER) && (WINVER <= 0x0400)
#undef HAVE_MONITOR_INFO
#endif
#ifdef HAVE_MONITOR_INFO
typedef BOOL (WINAPI *t_EnumDisplayMonitors)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
typedef BOOL (WINAPI *t_GetMonitorInfoA)(HMONITOR, LPMONITORINFO);
static t_EnumDisplayMonitors p_EnumDisplayMonitors = NULL;
static t_GetMonitorInfoA p_GetMonitorInfoA = NULL;
#endif
void
_gdk_windowing_set_default_display (GdkDisplay *display)
{
g_assert (_gdk_display == display);
}
#ifdef HAVE_MONITOR_INFO
static BOOL CALLBACK
count_monitor (HMONITOR hmonitor,
HDC hdc,
LPRECT rect,
LPARAM data)
{
gint *n = (gint *) data;
(*n)++;
return TRUE;
}
static BOOL CALLBACK
enum_monitor (HMONITOR hmonitor,
HDC hdc,
LPRECT rect,
LPARAM data)
{
MONITORINFOEX monitor_info;
gint *index = (gint *) data;
GdkRectangle *monitor;
g_assert (*index < _gdk_num_monitors);
monitor = _gdk_monitors + *index;
monitor_info.cbSize = sizeof (MONITORINFOEX);
(*p_GetMonitorInfoA) (hmonitor, (MONITORINFO *) &monitor_info);
#ifndef MONITORINFOF_PRIMARY
#define MONITORINFOF_PRIMARY 1
#endif
monitor->x = monitor_info.rcMonitor.left;
monitor->y = monitor_info.rcMonitor.top;
monitor->width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left;
monitor->height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top;
if (monitor_info.dwFlags & MONITORINFOF_PRIMARY &&
*index != 0)
{
/* Put primary monitor at index 0, just in case somebody needs
* to know which one is the primary.
*/
GdkRectangle temp = *monitor;
*monitor = _gdk_monitors[0];
_gdk_monitors[0] = temp;
}
(*index)++;
return TRUE;
}
#endif /* HAVE_MONITOR_INFO */
void
_gdk_monitor_init (void)
{
#ifdef HAVE_MONITOR_INFO
static HMODULE user32 = NULL;
if (user32 == NULL)
{
user32 = GetModuleHandle ("user32.dll");
g_assert (user32 != NULL);
p_EnumDisplayMonitors = (t_EnumDisplayMonitors) GetProcAddress (user32, "EnumDisplayMonitors");
p_GetMonitorInfoA = (t_GetMonitorInfoA) GetProcAddress (user32, "GetMonitorInfoA");
}
if (p_EnumDisplayMonitors != NULL && p_GetMonitorInfoA != NULL)
{
gint i, index;
_gdk_num_monitors = 0;
(*p_EnumDisplayMonitors) (NULL, NULL, count_monitor, (LPARAM) &_gdk_num_monitors);
_gdk_monitors = g_renew (GdkRectangle, _gdk_monitors, _gdk_num_monitors);
index = 0;
(*p_EnumDisplayMonitors) (NULL, NULL, enum_monitor, (LPARAM) &index);
_gdk_offset_x = G_MININT;
_gdk_offset_y = G_MININT;
/* Calculate offset */
for (i = 0; i < _gdk_num_monitors; i++)
{
_gdk_offset_x = MAX (_gdk_offset_x, -_gdk_monitors[i].x);
_gdk_offset_y = MAX (_gdk_offset_y, -_gdk_monitors[i].y);
}
GDK_NOTE (MISC, g_print ("Multi-monitor offset: (%d,%d)\n",
_gdk_offset_x, _gdk_offset_y));
/* Translate monitor coords into GDK coordinate space */
for (i = 0; i < _gdk_num_monitors; i++)
{
_gdk_monitors[i].x += _gdk_offset_x;
_gdk_monitors[i].y += _gdk_offset_y;
GDK_NOTE (MISC, g_print ("Monitor %d: %dx%d@%+d%+d\n",
i, _gdk_monitors[i].width,
_gdk_monitors[i].height,
_gdk_monitors[i].x, _gdk_monitors[i].y));
}
}
else
#endif /* HAVE_MONITOR_INFO */
{
unsigned int width, height;
_gdk_num_monitors = 1;
_gdk_monitors = g_renew (GdkRectangle, _gdk_monitors, 1);
width = GetSystemMetrics (SM_CXSCREEN);
height = GetSystemMetrics (SM_CYSCREEN);
_gdk_monitors[0].x = 0;
_gdk_monitors[0].y = 0;
_gdk_monitors[0].width = width;
_gdk_monitors[0].height = height;
_gdk_offset_x = 0;
_gdk_offset_y = 0;
}
}
/*
* Dynamic version of ProcessIdToSessionId() form Terminal Service.
* It is only returning something else than 0 when running under
* Terminal Service, available since NT4 SP4 and not for win9x
*/
static guint
get_session_id (void)
{
typedef BOOL (WINAPI *t_ProcessIdToSessionId) (DWORD, DWORD*);
static t_ProcessIdToSessionId p_ProcessIdToSessionId = NULL;
static HMODULE kernel32 = NULL;
DWORD id = 0;
if (kernel32 == NULL)
{
kernel32 = GetModuleHandle ("kernel32.dll");
g_assert (kernel32 != NULL);
p_ProcessIdToSessionId = (t_ProcessIdToSessionId) GetProcAddress (kernel32, "ProcessIdToSessionId");
}
if (p_ProcessIdToSessionId)
p_ProcessIdToSessionId (GetCurrentProcessId (), &id); /* got it (or not ;) */
return id;
}
GdkDisplay *
gdk_display_open (const gchar *display_name)
{
GDK_NOTE (MISC, g_print ("gdk_display_open: %s\n", (display_name ? display_name : "NULL")));
if (display_name == NULL ||
g_ascii_strcasecmp (display_name,
gdk_display_get_name (_gdk_display)) == 0)
{
if (_gdk_display != NULL)
{
GDK_NOTE (MISC, g_print ("... return _gdk_display\n"));
return _gdk_display;
}
}
else
{
GDK_NOTE (MISC, g_print ("... return NULL\n"));
return NULL;
}
_gdk_display = g_object_new (GDK_TYPE_DISPLAY, NULL);
_gdk_screen = g_object_new (GDK_TYPE_SCREEN, NULL);
_gdk_monitor_init ();
_gdk_visual_init ();
gdk_screen_set_default_colormap (_gdk_screen,
gdk_screen_get_system_colormap (_gdk_screen));
_gdk_windowing_window_init ();
_gdk_windowing_image_init ();
_gdk_events_init ();
_gdk_input_init (_gdk_display);
_gdk_dnd_init ();
/* Precalculate display name */
(void) gdk_display_get_name (_gdk_display);
g_signal_emit_by_name (gdk_display_manager_get (),
"display_opened", _gdk_display);
GDK_NOTE (MISC, g_print ("... _gdk_display now set up\n"));
return _gdk_display;
}
G_CONST_RETURN gchar *
gdk_display_get_name (GdkDisplay *display)
{
HDESK hdesk = GetThreadDesktop (GetCurrentThreadId ());
char dummy;
char *desktop_name;
HWINSTA hwinsta = GetProcessWindowStation ();
char *window_station_name;
DWORD n;
char *display_name;
static const char *display_name_cache = NULL;
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
if (display_name_cache != NULL)
return display_name_cache;
n = 0;
GetUserObjectInformation (hdesk, UOI_NAME, &dummy, 0, &n);
if (n == 0)
desktop_name = "Default";
else
{
n++;
desktop_name = g_alloca (n + 1);
memset (desktop_name, 0, n + 1);
if (!GetUserObjectInformation (hdesk, UOI_NAME, desktop_name, n, &n))
desktop_name = "Default";
}
n = 0;
GetUserObjectInformation (hwinsta, UOI_NAME, &dummy, 0, &n);
if (n == 0)
window_station_name = "WinSta0";
else
{
n++;
window_station_name = g_alloca (n + 1);
memset (window_station_name, 0, n + 1);
if (!GetUserObjectInformation (hwinsta, UOI_NAME, window_station_name, n, &n))
window_station_name = "WinSta0";
}
display_name = g_strdup_printf ("%d\\%s\\%s",
get_session_id (), window_station_name,
desktop_name);
GDK_NOTE (MISC, g_print ("gdk_display_get_name: %s\n", display_name));
display_name_cache = display_name;
return display_name_cache;
}
gint
gdk_display_get_n_screens (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
return 1;
}
GdkScreen *
gdk_display_get_screen (GdkDisplay *display,
gint screen_num)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
g_return_val_if_fail (screen_num != 0, NULL);
return _gdk_screen;
}
GdkScreen *
gdk_display_get_default_screen (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
return _gdk_screen;
}
GdkWindow *
gdk_display_get_default_group (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
g_warning ("gdk_display_get_default_group not yet implemented");
return NULL;
}
gboolean
gdk_display_supports_selection_notification (GdkDisplay *display)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
return TRUE;
}
static HWND _hwnd_next_viewer = NULL;
/*
* maybe this should be integrated with the default message loop - or maybe not ;-)
*/
static LRESULT CALLBACK
_win32_on_clipboard_change (HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam)
{
switch (message)
{
case WM_DESTROY : /* remove us from chain */
{
ChangeClipboardChain (hwnd, _hwnd_next_viewer);
return 0;
}
case WM_CHANGECBCHAIN :
{
HWND hwndRemove = (HWND) wparam; /* handle of window being removed */
HWND hwndNext = (HWND) lparam; /* handle of next window in chain */
if (hwndRemove == _hwnd_next_viewer)
_hwnd_next_viewer = hwndNext == hwnd ? NULL : hwndNext;
return 0;
}
case WM_DRAWCLIPBOARD :
{
/* Create the appropriate gdk events */
#ifdef G_ENABLE_DEBUG
if ((_gdk_debug_flags & GDK_DEBUG_DND) &&
OpenClipboard (hwnd))
{
HWND hwndOwner = GetClipboardOwner ();
UINT nFormat = 0;
g_print ("WM_DRAWCLIPBOARD: owner:%p formats: ", hwndOwner);
for (; 0 != (nFormat = EnumClipboardFormats (nFormat));)
{
g_print ("%s ", _gdk_win32_cf_to_string (nFormat));
}
g_print ("\n");
CloseClipboard ();
}
#endif
/* XXX: generate the apropriate GdkEventOwnerChange ... */
/* don't break the chain */
return PostMessage (_hwnd_next_viewer, message, wparam, lparam);
}
default :
return DefWindowProc (hwnd, message, wparam, lparam);
}
}
/*
* Creates a hidden window and adds it to the clipboard chain
*/
HWND
_gdk_win32_register_clipboard_notification (void)
{
WNDCLASS wclass;
HWND hwnd;
ATOM klass;
memset (&wclass, 0, sizeof(WNDCLASS));
wclass.lpszClassName = "GdkClipboardNotification";
wclass.lpfnWndProc = _win32_on_clipboard_change;
wclass.hInstance = _gdk_app_hmodule;
klass = RegisterClass (&wclass);
if (!klass)
return NULL;
hwnd = CreateWindow (MAKEINTRESOURCE(klass),
NULL, WS_POPUP,
0, 0, 0, 0, NULL, NULL,
_gdk_app_hmodule, NULL);
if (!hwnd)
{
UnregisterClass (MAKEINTRESOURCE(klass), _gdk_app_hmodule);
return NULL;
}
_hwnd_next_viewer = SetClipboardViewer (hwnd);
return hwnd;
}
/*
* The whole function would only make sense if the gdk/win32 clipboard
* model is rewritten to do delayed rendering. Currently this is only
* testcode and as noted in
* http://mail.gnome.org/archives/gtk-devel-list/2004-May/msg00113.html
* probably not worth bothering ;)
*/
gboolean
gdk_display_request_selection_notification (GdkDisplay *display,
GdkAtom selection)
{
static HWND hwndViewer = NULL;
gboolean ret = FALSE;
GDK_NOTE (DND,
g_print ("gdk_display_request_selection_notification (..., %s)",
gdk_atom_name (selection)));
if (GDK_SELECTION_CLIPBOARD == selection)
{
if (!hwndViewer)
{
hwndViewer = _gdk_win32_register_clipboard_notification ();
GDK_NOTE (DND, g_print (" registered"));
}
ret = (hwndViewer != NULL);
}
else if (GDK_SELECTION_PRIMARY == selection)
{
/* seems to work by default ? */
GDK_NOTE (DND, g_print (" by default"));
ret = TRUE;
}
GDK_NOTE (DND, g_print (" -> %s\n", ret ? "TRUE" : "FALSE"));
return ret;
}
gboolean
gdk_display_supports_clipboard_persistence (GdkDisplay *display)
{
return FALSE;
}
void
gdk_display_store_clipboard (GdkDisplay *display,
GdkWindow *clipboard_window,
guint32 time_,
GdkAtom *targets,
gint n_targets)
{
/* XXX: implement it (or maybe not as long as we don't support delayed rendering?) */
}