/* GDM - The GNOME Display Manager * Copyright (C) 1998, 1999, 2000 Martin K. Petersen * * This file Copyright (c) 2001 George Lebl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_XFREE_XINERAMA #include #elif HAVE_SOLARIS_XINERAMA #include #endif #include "gdmwm.h" #include "gdm.h" #include "gdmcommon.h" #include "gdmconfig.h" #include "gdm-common.h" typedef struct _GdmWindow GdmWindow; struct _GdmWindow { int x, y; Window win; Window deco; Window shadow; gboolean ignore_size_hints; /* for gdm windows */ gboolean center; /* do centering */ gboolean recenter; /* do re-centering */ gboolean takefocus; /* permit take focus */ /* hack, when we reparent, we will get an unmap and then * an map, and we want to ignore those */ int ignore_next_map; int ignore_next_unmap; }; static GList *windows = NULL; static gboolean focus_new_windows = FALSE; static int no_focus_login = 0; static Display *wm_disp = NULL; static Window wm_root = None; static Window wm_login_window = None; static Window wm_focus_window = None; static Atom XA_WM_PROTOCOLS = 0; static Atom XA_WM_STATE = 0; static Atom XA_WM_TAKE_FOCUS = 0; static Atom XA_COMPOUND_TEXT = 0; static Atom XA_NET_WM_STRUT = 0; static int trap_depth = 0; GdkRectangle *gdm_wm_allscreens = NULL; int gdm_wm_screens = 0; GdkRectangle gdm_wm_screen = {0,0,0,0}; static Window strut_owners[4] = {None, None, None, None}; static guint save_struts[4] = {0, 0, 0, 0}; void gdm_wm_screen_init (int cur_screen_num) { if (g_getenv ("FAKE_XINERAMA_GDM") != NULL) { /* for testing Xinerama support on non-xinerama setups */ gdm_wm_screen.x = 100; gdm_wm_screen.y = 100; gdm_wm_screen.width = gdk_screen_width () / 2 - 100; gdm_wm_screen.height = gdk_screen_height () / 2 - 100; gdm_wm_allscreens = g_new0 (GdkRectangle, 2); gdm_wm_allscreens[0] = gdm_wm_screen; gdm_wm_allscreens[1].x = gdk_screen_width () / 2; gdm_wm_allscreens[1].y = gdk_screen_height () / 2; gdm_wm_allscreens[1].width = gdk_screen_width () / 2; gdm_wm_allscreens[1].height = gdk_screen_height () / 2; gdm_wm_screens = 2; return; } { #ifdef HAVE_XFREE_XINERAMA gboolean have_xinerama = FALSE; gdk_flush (); gdk_error_trap_push (); have_xinerama = XineramaIsActive (GDK_DISPLAY ()); gdk_flush (); if (gdk_error_trap_pop () != 0) have_xinerama = FALSE; if (have_xinerama) { int screen_num, i; XineramaScreenInfo *xscreens = XineramaQueryScreens (GDK_DISPLAY (), &screen_num); if (screen_num <= 0) { /* should NEVER EVER happen */ gdm_common_error ("Xinerama active, but <= 0 screens?"); gdm_wm_screen.x = 0; gdm_wm_screen.y = 0; gdm_wm_screen.width = gdk_screen_width (); gdm_wm_screen.height = gdk_screen_height (); gdm_wm_allscreens = g_new0 (GdkRectangle, 1); gdm_wm_allscreens[0] = gdm_wm_screen; gdm_wm_screens = 1; return; } if (screen_num <= cur_screen_num) cur_screen_num = 0; gdm_wm_allscreens = g_new0 (GdkRectangle, screen_num); gdm_wm_screens = screen_num; for (i = 0; i < screen_num; i++) { gdm_wm_allscreens[i].x = xscreens[i].x_org; gdm_wm_allscreens[i].y = xscreens[i].y_org; gdm_wm_allscreens[i].width = xscreens[i].width; gdm_wm_allscreens[i].height = xscreens[i].height; if (cur_screen_num == i) gdm_wm_screen = gdm_wm_allscreens[i]; } XFree (xscreens); } else #elif HAVE_SOLARIS_XINERAMA gboolean have_xinerama = FALSE; /* This code from GDK, Copyright (C) 2002 Sun Microsystems */ int opcode; int firstevent; int firsterror; int n_monitors = 0; gdk_flush (); gdk_error_trap_push (); have_xinerama = XQueryExtension (GDK_DISPLAY (), "XINERAMA", &opcode, &firstevent, &firsterror); gdk_flush (); if (gdk_error_trap_pop () != 0) have_xinerama = FALSE; if (have_xinerama) { int i; int result; XRectangle monitors[MAXFRAMEBUFFERS]; unsigned char hints[16]; result = XineramaGetInfo (GDK_DISPLAY (), 0, monitors, hints, &n_monitors); /* Yes I know it should be Success but the current implementation * returns the num of monitor */ if (result <= 0) { /* should NEVER EVER happen */ gdm_common_error ("Xinerama active, but <= 0 screens?"); gdm_wm_screen.x = 0; gdm_wm_screen.y = 0; gdm_wm_screen.width = gdk_screen_width (); gdm_wm_screen.height = gdk_screen_height (); gdm_wm_allscreens = g_new0 (GdkRectangle, 1); gdm_wm_allscreens[0] = gdm_wm_screen; gdm_wm_screens = 1; return; } if (n_monitors <= cur_screen_num) cur_screen_num = 0; gdm_wm_allscreens = g_new0 (GdkRectangle, n_monitors); gdm_wm_screens = n_monitors; for (i = 0; i < n_monitors; i++) { gdm_wm_allscreens[i].x = monitors[i].x; gdm_wm_allscreens[i].y = monitors[i].y; gdm_wm_allscreens[i].width = monitors[i].width; gdm_wm_allscreens[i].height = monitors[i].height; if (cur_screen_num == i) gdm_wm_screen = gdm_wm_allscreens[i]; } } else #endif { gdm_wm_screen.x = 0; gdm_wm_screen.y = 0; gdm_wm_screen.width = gdk_screen_width (); gdm_wm_screen.height = gdk_screen_height (); gdm_wm_allscreens = g_new0 (GdkRectangle, 1); gdm_wm_allscreens[0] = gdm_wm_screen; gdm_wm_screens = 1; } } } void gdm_wm_set_screen (int cur_screen_num) { if (cur_screen_num >= gdm_wm_screens || cur_screen_num < 0) cur_screen_num = 0; gdm_wm_screen = gdm_wm_allscreens[cur_screen_num]; } /* Not really a WM function, center a gtk window by setting uposition */ void gdm_wm_center_window (GtkWindow *cw) { gint x, y; gint w, h; gtk_window_get_size (cw, &w, &h); x = gdm_wm_screen.x + (gdm_wm_screen.width - w)/2; y = gdm_wm_screen.y + (gdm_wm_screen.height - h)/2; if (x < gdm_wm_screen.x) x = gdm_wm_screen.x; if (y < gdm_wm_screen.y) y = gdm_wm_screen.y; gtk_window_move (GTK_WINDOW (cw), x, y); } void gdm_wm_center_cursor (void) { XWarpPointer (wm_disp, None, wm_root, 0, 0, 0, 0, gdm_wm_screen.x + gdm_wm_screen.width / 2, gdm_wm_screen.y + gdm_wm_screen.height / 2); } static void trap_push (void) { trap_depth++; gdk_error_trap_push (); } static int trap_pop (void) { trap_depth --; if (trap_depth <= 0) XSync (wm_disp, False); return gdk_error_trap_pop (); } /* stolen from gwmh */ static gpointer get_typed_property_data (Display *xdisplay, Window xwindow, Atom property, Atom requested_type, gint *size_p, guint expected_format) { static const guint prop_buffer_lengh = 1024 * 1024; unsigned char *prop_data = NULL; Atom type_returned = 0; unsigned long nitems_return = 0, bytes_after_return = 0; int format_returned = 0; gpointer data = NULL; gboolean abort = FALSE; g_return_val_if_fail (size_p != NULL, NULL); *size_p = 0; gdk_error_trap_push (); abort = XGetWindowProperty (xdisplay, xwindow, property, 0, prop_buffer_lengh, False, requested_type, &type_returned, &format_returned, &nitems_return, &bytes_after_return, &prop_data) != Success; if (gdk_error_trap_pop () || type_returned == None) abort++; if (!abort && requested_type != AnyPropertyType && requested_type != type_returned) { /* aparently this can happen for some properties of broken apps, be silent */ abort++; } if (!abort && bytes_after_return) { g_warning (G_GNUC_PRETTY_FUNCTION "(): Eeek, property has more than %u bytes, stored on harddisk?", prop_buffer_lengh); abort++; } if (!abort && expected_format && expected_format != format_returned) { g_warning (G_GNUC_PRETTY_FUNCTION "(): Expected format (%u) unmatched (%d)", expected_format, format_returned); abort++; } if (!abort && prop_data && nitems_return && format_returned) { switch (format_returned) { case 32: *size_p = nitems_return * 4; if (sizeof (gulong) == 8) { guint32 i, *mem = g_malloc0 (*size_p + 1); gulong *prop_longs = (gulong*) prop_data; for (i = 0; i < *size_p / 4; i++) mem[i] = prop_longs[i]; data = mem; } break; case 16: *size_p = nitems_return * 2; break; case 8: *size_p = nitems_return; break; default: g_warning ("Unknown property data format with %d bits (extraterrestrial?)", format_returned); break; } if (!data && *size_p) { guint8 *mem = NULL; if (format_returned == 8 && type_returned == XA_COMPOUND_TEXT) { gchar **tlist = NULL; gint count = gdk_text_property_to_text_list (gdk_x11_xatom_to_atom (type_returned), 8, prop_data, nitems_return, &tlist); if (count && tlist && tlist[0]) { mem = (guint8 *)g_strdup (tlist[0]); *size_p = strlen ((char *)mem); } if (tlist) gdk_free_text_list (tlist); } if (!mem) { mem = g_malloc (*size_p + 1); memcpy (mem, prop_data, *size_p); mem[*size_p] = 0; } data = mem; } } if (prop_data) XFree (prop_data); return data; } /* * Update the gdm_wm_screen 'effective' screen area when a window reserves struts. * This only works if struts don't "collide", i.e. there is a max of one strut setter * per edge. Of course this should be the case at gdm time... */ static void gdm_wm_update_struts (Display *xdisplay, Window xwindow) { gint size = 0; guint32 *struts = get_typed_property_data (xdisplay, xwindow, XA_NET_WM_STRUT, XA_CARDINAL, &size, 32); if (size == 16) { gint i; for (i = 0; i < 4; ++i) { /* strut owners are the only windows whose 'zero' struts are reflected */ if (struts[i] != 0 || (strut_owners[i] == xwindow)) { /* if any window re-specifies a strut, it becomes the new owner */ strut_owners[i] = xwindow; save_struts[i] = struts[i]; } } } g_free (struts); } /* stolen from gwmh */ static gboolean wm_protocol_check_support (Window xwin, Atom check_atom) { Atom *pdata = NULL; guint32 *gdata = NULL; int n_pids = 0; gboolean is_supported = FALSE; guint i, n_gids = 0; trap_push (); if (!XGetWMProtocols (wm_disp, xwin, &pdata, &n_pids)) { gint size = 0; gdata = get_typed_property_data (wm_disp, xwin, XA_WM_PROTOCOLS, XA_WM_PROTOCOLS, &size, 32); n_gids = size / 4; } trap_pop (); for (i = 0; i < n_pids; i++) if (pdata[i] == check_atom) { is_supported = TRUE; break; } if (pdata) XFree (pdata); if (!is_supported) for (i = 0; i < n_gids; i++) if (gdata[i] == check_atom) { is_supported = TRUE; break; } g_free (gdata); return is_supported; } static GList * find_window_list (Window w, gboolean deco_ok) { GList *li; for (li = windows; li != NULL; li = li->next) { GdmWindow *gw = li->data; if (gw->win == w) return li; if (deco_ok && (gw->deco == w || gw->shadow == w)) return li; } return NULL; } static GdmWindow * find_window (Window w, gboolean deco_ok) { GList *li = find_window_list (w, deco_ok); if (li == NULL) return NULL; else return li->data; } void gdm_wm_focus_window (Window window) { XWindowAttributes attribs = {0}; GdmWindow *win; if (no_focus_login > 0 && window == wm_login_window) return; win = find_window (window, TRUE); if (win != NULL && ! win->takefocus) return; trap_push (); XGetWindowAttributes (wm_disp, window, &attribs); if (attribs.map_state == IsUnmapped) { trap_pop (); return; } if (wm_protocol_check_support (window, XA_WM_TAKE_FOCUS)) { XEvent xevent = { 0, }; xevent.type = ClientMessage; xevent.xclient.window = window; xevent.xclient.message_type = XA_WM_PROTOCOLS; xevent.xclient.format = 32; xevent.xclient.data.l[0] = XA_WM_TAKE_FOCUS; xevent.xclient.data.l[1] = CurrentTime; XSendEvent (wm_disp, window, False, 0, &xevent); XSync (wm_disp, False); } XSetInputFocus (wm_disp, window, RevertToPointerRoot, CurrentTime); trap_pop (); wm_focus_window = window; } static void constrain_window (GdmWindow *gw) { /* constrain window to lie within screen geometry, with struts reserved */ int x, y, screen_x = 0, screen_y = 0; Window root; unsigned int width, height, border, depth; unsigned int screen_width = gdk_screen_width (), screen_height = gdk_screen_height (); /* exclude any strut areas not owned by this window */ if (strut_owners[0] != gw->win) { screen_x = save_struts[0]; screen_width -= save_struts[0]; } if (strut_owners[2] != gw->win) { screen_y = save_struts[2]; screen_height -= save_struts[2]; } if (strut_owners[1] != gw->win) screen_width -= save_struts[1]; if (strut_owners[3] != gw->win) screen_height -= save_struts[3]; if (gw->deco == None) return; trap_push (); XGetGeometry (wm_disp, gw->deco, &root, &x, &y, &width, &height, &border, &depth); if (width > screen_width) width = screen_width; if (height > screen_height) height = screen_height; if (x < screen_x) x = screen_x; if (y < screen_y) y = screen_y; if ((x - screen_x + width) > screen_width) x = screen_width - width; if ((y - screen_y + height) > screen_height) y = screen_height - height; XMoveResizeWindow (wm_disp, gw->deco, x, y, width, height); trap_pop (); } static void constrain_all_windows (void) { GList *winlist = windows; while (winlist) { GdmWindow *gw = winlist->data; constrain_window (gw); winlist = winlist->next; } } static void center_x_window (GdmWindow *gw, Window w, Window hintwin) { XSizeHints hints; Status status; long ret; int x, y; Window root; unsigned int width, height, border, depth; gboolean can_resize, can_reposition; trap_push (); status = XGetWMNormalHints (wm_disp, hintwin, &hints, &ret); if ( ! status) { trap_pop (); return; } /* allow resizing when PSize is given, just don't allow centering when * PPosition is goven */ can_resize = ! (hints.flags & USSize); can_reposition = ! (hints.flags & USPosition || hints.flags & PPosition); if (can_reposition && ! gw->center) can_reposition = FALSE; if (gw->ignore_size_hints) { can_resize = TRUE; can_reposition = TRUE; } if ( ! can_resize && ! can_reposition) { trap_pop (); return; } XGetGeometry (wm_disp, w, &root, &x, &y, &width, &height, &border, &depth); /* we replace the x,y and width,height with some new values */ if (can_resize) { if (width > gdm_wm_screen.width) width = gdm_wm_screen.width; if (height > gdm_wm_screen.height) height = gdm_wm_screen.height; } if (can_reposition) { /* we wipe the X with some new values */ x = gdm_wm_screen.x + (gdm_wm_screen.width - width)/2; y = gdm_wm_screen.y + (gdm_wm_screen.height - height)/2; if (x < gdm_wm_screen.x) x = gdm_wm_screen.x; if (y < gdm_wm_screen.y) y = gdm_wm_screen.y; } XMoveResizeWindow (wm_disp, w, x, y, width, height); if (gw->center && ! gw->recenter) { gw->center = FALSE; } trap_pop (); } #ifndef MWMUTIL_H_INCLUDED typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } MotifWmHints, MwmHints; #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_DECOR_BORDER (1L << 1) #endif /* MWMUTIL_H_INCLUDED */ static gboolean has_deco (Window win) { static Atom hints_atom = None; unsigned char *foo; MotifWmHints *hints; Atom type; gint format; gulong nitems; gulong bytes_after; gboolean border = TRUE; trap_push (); if (hints_atom == None) hints_atom = XInternAtom (wm_disp, "_MOTIF_WM_HINTS", False); hints = NULL; XGetWindowProperty (wm_disp, win, hints_atom, 0, sizeof (MotifWmHints) / sizeof (long), False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &foo); hints = (MotifWmHints *)foo; if (type != None && hints != NULL && hints->flags & MWM_HINTS_DECORATIONS && ! (hints->decorations & MWM_DECOR_BORDER)) { border = FALSE; } if (hints != NULL) XFree (hints); trap_pop (); return border; } static void add_deco (GdmWindow *w, gboolean is_mapped) { int x, y; Window root; unsigned int width, height, border, depth; XWindowAttributes attribs = { 0, }; int black; trap_push (); XGetWindowAttributes (wm_disp, w->win, &attribs); XSelectInput (wm_disp, w->win, attribs.your_event_mask | PropertyChangeMask); if ( ! has_deco (w->win)) { trap_pop (); return; } XGetGeometry (wm_disp, w->win, &root, &x, &y, &width, &height, &border, &depth); black = BlackPixel (wm_disp, DefaultScreen (wm_disp)); /* all but the login window has shadows */ if (w->win != wm_login_window) { w->shadow = XCreateSimpleWindow (wm_disp, wm_root, x + 4, y + 4, width + 2 + 2 * border, height + 2 + 2 * border, 0, black, black); XMapWindow (wm_disp, w->shadow); } w->deco = XCreateSimpleWindow (wm_disp, wm_root, x - 1, y - 1, width + 2 + 2 * border, height + 2 + 2 * border, 0, black, black); XGetWindowAttributes (wm_disp, w->deco, &attribs); XSelectInput (wm_disp, w->deco, attribs.your_event_mask | EnterWindowMask | PropertyChangeMask | SubstructureNotifyMask | SubstructureRedirectMask); XMapWindow (wm_disp, w->deco); XSync (wm_disp, False); trap_pop (); trap_push (); XReparentWindow (wm_disp, w->win, w->deco, 1, 1); XSync (wm_disp, False); if (trap_pop () == 0) { if (is_mapped) { /* Ignore the next unmap/map, but only * if reparent window really succeeded */ w->ignore_next_map++; w->ignore_next_unmap++; } } } static gboolean is_wm_class (XClassHint *hint, const char *string, int len) { if (len > 0) { return ((hint->res_name != NULL && strncmp (hint->res_name, string, len) == 0) || (hint->res_class != NULL && strncmp (hint->res_class, string, len) == 0)); } else { return ((hint->res_name != NULL && strcmp (hint->res_name, string) == 0) || (hint->res_class != NULL && strcmp (hint->res_class, string) == 0)); } } static GdmWindow * add_window (Window w, gboolean center, gboolean is_mapped) { GdmWindow *gw; gw = find_window (w, FALSE); if (gw == NULL) { XClassHint hint = { NULL, NULL }; XWMHints *wmhints; int x, y; Window root; unsigned int width, height, border, depth; gw = g_new0 (GdmWindow, 1); gw->win = w; windows = g_list_prepend (windows, gw); trap_push (); /* add "centering" */ gw->ignore_size_hints = FALSE; gw->center = center; gw->recenter = FALSE; gw->takefocus = TRUE; gw->ignore_next_map = 0; gw->ignore_next_unmap = 0; wmhints = XGetWMHints (wm_disp, w); if (wmhints != NULL) { /* NoInput windows */ if ((wmhints->flags & InputHint) && ! wmhints->input) { gw->takefocus = FALSE; } XFree (wmhints); } /* hack, set USpos/size on login window */ if (w == wm_login_window) { long ret; XSizeHints hints; XGetWMNormalHints (wm_disp, w, &hints, &ret); hints.flags |= USPosition | USSize; XSetWMNormalHints (wm_disp, w, &hints); gw->center = FALSE; gw->recenter = FALSE; } else if (XGetClassHint (wm_disp, w, &hint)) { if (is_wm_class (&hint, "gdm", 3)) { gw->ignore_size_hints = TRUE; gw->center = TRUE; gw->recenter = TRUE; } else if (is_wm_class (&hint, "gkrellm", 0)) { /* hack, gkrell is stupid and doesn't set * right hints, such as USPosition and other * such stuff */ gw->center = FALSE; gw->recenter = FALSE; } else if (is_wm_class (&hint, "xscribble", 0)) { /* hack, xscribble mustn't take focus */ gw->takefocus = FALSE; } if (hint.res_name != NULL) XFree (hint.res_name); if (hint.res_class != NULL) XFree (hint.res_class); } XGetGeometry (wm_disp, w, &root, &x, &y, &width, &height, &border, &depth); gw->x = x; gw->y = x; center_x_window (gw, w, w); add_deco (gw, is_mapped); XAddToSaveSet (wm_disp, w); trap_pop (); } return gw; } static void remove_window (Window w) { GList *li = find_window_list (w, FALSE); if (w == wm_focus_window) wm_focus_window = None; if (li != NULL) { GdmWindow *gw = li->data; li->data = NULL; trap_push (); XRemoveFromSaveSet (wm_disp, w); gw->win = None; if (gw->deco != None) { XDestroyWindow (wm_disp, gw->deco); gw->deco = None; } if (gw->shadow != None) { XDestroyWindow (wm_disp, gw->shadow); gw->shadow = None; } trap_pop (); windows = g_list_remove_link (windows, li); g_list_free_1 (li); g_free (gw); } } static void revert_focus_to_login (void) { if (wm_login_window != None) { gdm_wm_focus_window (wm_login_window); } } static void add_all_current_windows (void) { Window *children = NULL; Window xparent, xroot; guint size = 0; gdk_flush (); XSync (wm_disp, False); trap_push (); XGrabServer (wm_disp); if (XQueryTree (wm_disp, wm_root, &xroot, &xparent, &children, &size)) { int i; for (i = 0; i < size; i++) { XWindowAttributes attribs = {0}; XGetWindowAttributes (wm_disp, children[i], &attribs); if ( ! attribs.override_redirect && attribs.map_state != IsUnmapped) { add_window (children[i], FALSE /*center*/, TRUE /* is_mapped */); } } if (children != NULL) XFree (children); } XUngrabServer (wm_disp); trap_pop (); } static void reparent_to_root (GdmWindow *gw) { /* only if reparented */ if (gw->deco != None) { trap_push (); XReparentWindow (wm_disp, gw->win, wm_root, gw->x, gw->y); XSync (wm_disp, False); trap_pop (); } } static void shadow_follow (GdmWindow *gw) { int x, y; Window root; unsigned int width, height, border, depth; if (gw->shadow == None) return; trap_push (); XGetGeometry (wm_disp, gw->deco, &root, &x, &y, &width, &height, &border, &depth); x += 5; y += 5; XMoveResizeWindow (wm_disp, gw->shadow, x, y, width, height); trap_pop (); } static void event_process (XEvent *ev) { GdmWindow *gw; Window w; XWindowChanges wchanges; trap_push (); switch (ev->type) { case MapRequest: w = ev->xmaprequest.window; gw = find_window (w, FALSE); if (gw == NULL) { if (ev->xmaprequest.parent == wm_root) { XGrabServer (wm_disp); gw = add_window (w, TRUE /* center */, FALSE /* is_mapped */); XUngrabServer (wm_disp); } } XMapWindow (wm_disp, w); break; case ConfigureRequest: XGrabServer (wm_disp); w = ev->xconfigurerequest.window; gw = find_window (w, FALSE); wchanges.border_width = ev->xconfigurerequest.border_width; wchanges.sibling = ev->xconfigurerequest.above; wchanges.stack_mode = ev->xconfigurerequest.detail; if (gw == NULL || gw->deco == None) { wchanges.x = ev->xconfigurerequest.x; wchanges.y = ev->xconfigurerequest.y; } else { wchanges.x = 1; wchanges.y = 1; } wchanges.width = ev->xconfigurerequest.width; wchanges.height = ev->xconfigurerequest.height; XConfigureWindow (wm_disp, w, ev->xconfigurerequest.value_mask, &wchanges); if (gw != NULL) { gw->x = ev->xconfigurerequest.x; gw->y = ev->xconfigurerequest.y; if (gw->deco != None) { wchanges.x = ev->xconfigurerequest.x - 1; wchanges.y = ev->xconfigurerequest.y - 1; wchanges.width = ev->xconfigurerequest.width + 2 + 2*ev->xconfigurerequest.border_width;; wchanges.height = ev->xconfigurerequest.height + 2 + 2*ev->xconfigurerequest.border_width;; wchanges.border_width = 0; XConfigureWindow (wm_disp, gw->deco, ev->xconfigurerequest.value_mask, &wchanges); center_x_window (gw, gw->deco, gw->win); } else { center_x_window (gw, gw->win, gw->win); } shadow_follow (gw); } XUngrabServer (wm_disp); break; case CirculateRequest: w = ev->xcirculaterequest.window; gw = find_window (w, FALSE); if (gw == NULL) { if (ev->xcirculaterequest.place == PlaceOnTop) XRaiseWindow (wm_disp, w); else XLowerWindow (wm_disp, w); } else { if (ev->xcirculaterequest.place == PlaceOnTop) { if (gw->shadow != None) XRaiseWindow (wm_disp, gw->shadow); if (gw->deco != None) XRaiseWindow (wm_disp, gw->deco); else XRaiseWindow (wm_disp, gw->win); } else { if (gw->deco != None) XLowerWindow (wm_disp, gw->deco); else XLowerWindow (wm_disp, gw->win); if (gw->shadow != None) XLowerWindow (wm_disp, gw->shadow); } } break; case MapNotify: w = ev->xmap.window; gw = find_window (w, FALSE); if (gw != NULL) { if (gw->ignore_next_map > 0) { gw->ignore_next_map --; break; } if ( ! ev->xmap.override_redirect && focus_new_windows) { gdm_wm_focus_window (w); } } break; case UnmapNotify: w = ev->xunmap.window; gw = find_window (w, FALSE); if (gw != NULL) { if (gw->ignore_next_unmap > 0) { gw->ignore_next_unmap --; break; } XGrabServer (wm_disp); if (gw->deco != None) XUnmapWindow (wm_disp, gw->deco); if (gw->shadow != None) XUnmapWindow (wm_disp, gw->shadow); reparent_to_root (gw); remove_window (w); XDeleteProperty (wm_disp, w, XA_WM_STATE); if (w != wm_login_window) revert_focus_to_login (); XUngrabServer (wm_disp); } break; case DestroyNotify: w = ev->xdestroywindow.window; gw = find_window (w, FALSE); if (gw != NULL) { XGrabServer (wm_disp); remove_window (w); if (w != wm_login_window) revert_focus_to_login (); XUngrabServer (wm_disp); } break; case EnterNotify: w = ev->xcrossing.window; gw = find_window (w, TRUE); if (gw != NULL) gdm_wm_focus_window (gw->win); break; case PropertyNotify: if (ev->xproperty.atom == XA_NET_WM_STRUT) { gdm_wm_update_struts (ev->xproperty.display, ev->xproperty.window); constrain_all_windows (); } break; default: break; } trap_pop (); } /* following partly stolen from gdk */ static GPollFD event_poll_fd; static gboolean event_prepare (GSource *source, gint *timeout) { *timeout = -1; return XPending (wm_disp) > 0; } static gboolean event_check (GSource *source) { if (event_poll_fd.revents & G_IO_IN) { return XPending (wm_disp) > 0; } else { return FALSE; } } static void process_events (void) { while (XPending (wm_disp) > 0) { XEvent ev; XNextEvent (wm_disp, &ev); event_process (&ev); } } static gboolean event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { process_events (); return TRUE; } static GSourceFuncs event_funcs = { event_prepare, event_check, event_dispatch }; void gdm_wm_init (Window login_window) { XWindowAttributes attribs = { 0, }; GSource *source; wm_login_window = login_window; if (wm_disp != NULL) { return; } wm_disp = XOpenDisplay (gdk_get_display ()); if (wm_disp == NULL) { /* EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEK! */ wm_disp = GDK_DISPLAY (); return; } trap_push (); XA_WM_PROTOCOLS = XInternAtom (wm_disp, "WM_PROTOCOLS", False); XA_WM_STATE = XInternAtom (wm_disp, "WM_STATE", False); XA_WM_TAKE_FOCUS = XInternAtom (wm_disp, "WM_TAKE_FOCUS", False); XA_COMPOUND_TEXT = XInternAtom (wm_disp, "COMPOUND_TEXT", False); XA_NET_WM_STRUT = XInternAtom (wm_disp, "_NET_WM_STRUT", False); wm_root = DefaultRootWindow (wm_disp); /* set event mask for events on root window */ XGetWindowAttributes (wm_disp, wm_root, &attribs); XSelectInput (wm_disp, wm_root, attribs.your_event_mask | SubstructureNotifyMask | SubstructureRedirectMask); if (trap_pop () != 0) return; trap_push (); add_all_current_windows (); source = g_source_new (&event_funcs, sizeof (GSource)); event_poll_fd.fd = ConnectionNumber (wm_disp); event_poll_fd.events = G_IO_IN; g_source_add_poll (source, &event_poll_fd); g_source_set_priority (source, GDK_PRIORITY_EVENTS); g_source_set_can_recurse (source, FALSE); g_source_attach (source, NULL); trap_pop (); } void gdm_wm_focus_new_windows (gboolean focus) { focus_new_windows = focus; } void gdm_wm_no_login_focus_push (void) { /* it makes not sense for this to be false then */ focus_new_windows = TRUE; no_focus_login++; } void gdm_wm_no_login_focus_pop (void) { no_focus_login --; if (no_focus_login == 0 && wm_focus_window == None && wm_login_window != None) gdm_wm_focus_window (wm_login_window); } void gdm_wm_get_window_pos (Window window, int *xp, int *yp) { int x, y; Window root; unsigned int width, height, border, depth; GdmWindow *gw; trap_push (); gw = find_window (window, TRUE); if (gw == NULL) { XGetGeometry (wm_disp, window, &root, &x, &y, &width, &height, &border, &depth); *xp = x; *yp = y; trap_pop (); return; } if (gw->deco != None) { XGetGeometry (wm_disp, gw->deco, &root, &x, &y, &width, &height, &border, &depth); *xp = x + 1; *yp = y + 1; } else { XGetGeometry (wm_disp, gw->win, &root, &x, &y, &width, &height, &border, &depth); *xp = x; *yp = y; } trap_pop (); } void gdm_wm_move_window_now (Window window, int x, int y) { GdmWindow *gw; trap_push (); gw = find_window (window, TRUE); if (gw == NULL) { XMoveWindow (wm_disp, window, x, y); XSync (wm_disp, False); trap_pop (); return; } if (gw->deco != None) XMoveWindow (wm_disp, gw->deco, x - 1, y - 1); else XMoveWindow (wm_disp, gw->win, x, y); if (gw->shadow != None) XMoveWindow (wm_disp, gw->deco, x + 4, y + 4); XSync (wm_disp, False); trap_pop (); } void gdm_wm_save_wm_order (void) { Window *children = NULL; Window xparent, xroot; guint size = 0; int dlen = 0; unsigned long *data; gdk_flush (); XSync (wm_disp, False); trap_push (); XGrabServer (wm_disp); if (XQueryTree (wm_disp, wm_root, &xroot, &xparent, &children, &size)) { int i; Atom atom; data = g_new0 (unsigned long, size); for (i = 0; i < size; i++) { GdmWindow *gw = find_window (children[i], TRUE); /* Ignore unknowns and shadows */ if (gw == NULL || gw->shadow == children[i]) continue; if (gw->win == wm_login_window) { /* Empty spot in the list signifies the * login window */ data [dlen++] = None; } else { data [dlen++] = gw->win; } } atom = XInternAtom (wm_disp, "GDMWM_WINDOW_ORDER", False); XChangeProperty (wm_disp, wm_root, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, dlen); if (children != NULL) XFree (children); g_free (data); } XUngrabServer (wm_disp); trap_pop (); } static gboolean focus_win (gpointer data) { Window focus = (Window)data; focus_new_windows = TRUE; gdm_wm_focus_window (focus); return FALSE; } void gdm_wm_restore_wm_order (void) { guint32 *data; Window focus = None; int size; int i; Atom atom; gdk_flush (); XSync (wm_disp, False); process_events (); gdk_flush (); XSync (wm_disp, False); trap_push (); XGrabServer (wm_disp); atom = XInternAtom (wm_disp, "GDMWM_WINDOW_ORDER", False); data = get_typed_property_data (wm_disp, wm_root, atom, XA_CARDINAL, &size, 32); if (data != NULL) { for (i = 0; i < size/4; i++) { GdmWindow *gw; if (data[i] == None) gw = find_window (wm_login_window, TRUE); else gw = find_window (data[i], TRUE); if (gw != NULL) { focus = gw->win; if (gw->shadow != None) XRaiseWindow (wm_disp, gw->shadow); if (gw->deco != None) XRaiseWindow (wm_disp, gw->deco); else XRaiseWindow (wm_disp, gw->win); } } g_free (data); } XUngrabServer (wm_disp); trap_pop (); process_events (); if (focus != None) { /* let us hit the main loop first */ g_idle_add (focus_win, (gpointer)focus); } } void gdm_wm_show_info_msg_dialog (const gchar *msg_file, const gchar *msg_font) { GtkWidget *dialog, *label; gchar *InfoMsg; gsize InfoMsgLength; if (ve_string_empty (msg_file) || ! g_file_test (msg_file, G_FILE_TEST_EXISTS) || ! g_file_get_contents (msg_file, &InfoMsg, &InfoMsgLength, NULL)) return; if (InfoMsgLength <= 0) { g_free (InfoMsg); return; } gdm_wm_focus_new_windows (TRUE); dialog = gtk_dialog_new_with_buttons (NULL /* Message */, NULL /* parent */, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); label = gtk_label_new (InfoMsg); if (msg_font && strlen (msg_font) > 0) { PangoFontDescription *GdmInfoMsgFontDesc = pango_font_description_from_string (msg_font); if (GdmInfoMsgFontDesc) { gtk_widget_modify_font (label, GdmInfoMsgFontDesc); pango_font_description_free (GdmInfoMsgFontDesc); } } gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label); gtk_widget_show_all (dialog); gdm_wm_center_window (GTK_WINDOW (dialog)); gdm_common_setup_cursor (GDK_LEFT_PTR); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gdm_wm_no_login_focus_pop (); g_free (InfoMsg); } static GtkWidget * hig_dialog_new (GtkWindow *parent, GtkDialogFlags flags, GtkMessageType type, GtkButtonsType buttons, const gchar *primary_message, const gchar *secondary_message) { GtkWidget *dialog; dialog = gtk_message_dialog_new (GTK_WINDOW (parent), GTK_DIALOG_DESTROY_WITH_PARENT, type, buttons, "%s", primary_message); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary_message); gtk_window_set_title (GTK_WINDOW (dialog), ""); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14); return dialog; } void gdm_wm_message_dialog (const gchar *primary_message, const gchar *secondary_message) { GtkWidget *req = NULL; /* we should be now fine for focusing new windows */ gdm_wm_focus_new_windows (TRUE); req = hig_dialog_new (NULL /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, primary_message, secondary_message); gdm_wm_center_window (GTK_WINDOW (req)); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (req)); gtk_widget_destroy (req); gdm_wm_no_login_focus_pop (); } gint gdm_wm_query_dialog (const gchar *primary_message, const gchar *secondary_message, const char *posbutton, const char *negbutton, gboolean has_cancel) { int ret; GtkWidget *req; GtkWidget *button; /* we should be now fine for focusing new windows */ gdm_wm_focus_new_windows (TRUE); req = hig_dialog_new (NULL /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, primary_message, secondary_message); if (negbutton != NULL) { button = gtk_button_new_from_stock (negbutton); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_NO); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (has_cancel == TRUE) { button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_CANCEL); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (posbutton != NULL) { button = gtk_button_new_from_stock (posbutton); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_YES); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (posbutton != NULL) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_YES); else if (negbutton != NULL) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_NO); else if (has_cancel) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_CANCEL); gdm_wm_center_window (GTK_WINDOW (req)); gdm_wm_no_login_focus_push (); ret = gtk_dialog_run (GTK_DIALOG (req)); gdm_wm_no_login_focus_pop (); gtk_widget_destroy (req); return ret; } gint gdm_wm_warn_dialog (const gchar *primary_message, const gchar *secondary_message, const char *posbutton, const char *negbutton, gboolean has_cancel) { int ret; GtkWidget *req; GtkWidget *button; /* we should be now fine for focusing new windows */ gdm_wm_focus_new_windows (TRUE); req = hig_dialog_new (NULL /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, primary_message, secondary_message); if (negbutton != NULL) { button = gtk_button_new_from_stock (negbutton); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_NO); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (has_cancel == TRUE) { button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_CANCEL); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (posbutton != NULL) { button = gtk_button_new_from_stock (posbutton); gtk_dialog_add_action_widget (GTK_DIALOG (req), button, GTK_RESPONSE_YES); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_show (button); } if (posbutton != NULL) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_YES); else if (negbutton != NULL) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_NO); else if (has_cancel) gtk_dialog_set_default_response (GTK_DIALOG (req), GTK_RESPONSE_CANCEL); gdm_wm_center_window (GTK_WINDOW (req)); gdm_wm_no_login_focus_push (); ret = gtk_dialog_run (GTK_DIALOG (req)); gdm_wm_no_login_focus_pop (); gtk_widget_destroy (req); return ret; } /* EOF */