diff --git a/src/display.c b/src/display.c index 6215be61f..19e88e402 100644 --- a/src/display.c +++ b/src/display.c @@ -68,7 +68,15 @@ meta_display_open (const char *name) GSList *screens; GSList *tmp; int i; - char *atom_names[] = { "_NET_WM_NAME", "WM_PROTOCOLS", "WM_TAKE_FOCUS", "WM_DELETE_WINDOW" }; + /* Remember to edit code that assigns each atom to display struct + * when adding an atom name here. + */ + char *atom_names[] = { + "_NET_WM_NAME", + "WM_PROTOCOLS", + "WM_TAKE_FOCUS", + "WM_DELETE_WINDOW" + }; Atom atoms[G_N_ELEMENTS(atom_names)]; meta_verbose ("Opening display '%s'\n", XDisplayName (name)); @@ -136,6 +144,9 @@ meta_display_open (const char *name) XInternAtoms (display->xdisplay, atom_names, G_N_ELEMENTS (atom_names), False, atoms); display->atom_net_wm_name = atoms[0]; + display->atom_wm_protocols = atoms[1]; + display->atom_wm_take_focus = atoms[2]; + display->atom_wm_delete_window = atoms[3]; /* Now manage all existing windows */ tmp = display->screens; @@ -184,6 +195,8 @@ meta_display_close (MetaDisplay *display) winlist = g_slist_sort (winlist, ptrcmp); + /* Unmanage all windows */ + meta_display_grab (display); tmp = winlist; while (tmp != NULL) { @@ -194,6 +207,7 @@ meta_display_close (MetaDisplay *display) tmp = tmp->next; } g_slist_free (winlist); + meta_display_ungrab (display); /* Must be after all calls to meta_window_free() since they * unregister windows @@ -397,10 +411,16 @@ event_queue_callback (MetaEventQueue *queue, meta_window_free (window); /* Unmanage destroyed window */ break; case UnmapNotify: - if (window) - meta_window_free (window); /* Unmanage withdrawn window */ + if (window && window->mapped) + { + meta_verbose ("Window %s withdrawn\n", + window->desc); + meta_window_free (window); /* Unmanage withdrawn window */ + } break; case MapNotify: + if (window) + window->mapped = TRUE; break; case MapRequest: if (window == NULL) @@ -414,6 +434,8 @@ event_queue_callback (MetaEventQueue *queue, /* Unmanage it, override_redirect was toggled on? * Can this happen? */ + meta_verbose ("Window %s toggled on override redirect\n", + window->desc); meta_window_free (window); } break; diff --git a/src/frame.c b/src/frame.c index 20e9202a6..92da35fa0 100644 --- a/src/frame.c +++ b/src/frame.c @@ -29,7 +29,7 @@ meta_frame_init_info (MetaFrame *frame, MetaFrameInfo *info) { info->flags = - META_FRAME_ALLOWS_DELETE | META_FRAME_ALLOWS_MENU | + META_FRAME_ALLOWS_MENU | META_FRAME_ALLOWS_DELETE | META_FRAME_ALLOWS_ICONIFY | META_FRAME_ALLOWS_MAXIMIZE | META_FRAME_ALLOWS_RESIZE; @@ -198,9 +198,6 @@ meta_window_ensure_frame (MetaWindow *window) if (window->frame) return; - - /* Need to fix Pango, it grabs the server */ - g_return_if_fail (window->display->server_grab_count == 0); frame = g_new (MetaFrame, 1); @@ -262,6 +259,9 @@ meta_window_ensure_frame (MetaWindow *window) meta_display_grab (window->display); meta_error_trap_push (window->display); + window->mapped = FALSE; /* the reparent will unmap the window, + * we don't want to take that as a withdraw + */ XReparentWindow (window->display->xdisplay, window->xwindow, frame->xwindow, @@ -274,8 +274,9 @@ meta_window_ensure_frame (MetaWindow *window) window->rect.y = frame->child_y; /* stick frame to the window */ - window->frame = frame; + window->frame = frame; + /* Put our state back where it should be */ if (window->iconic) meta_window_hide (window); else @@ -306,6 +307,10 @@ meta_window_destroy_frame (MetaWindow *window) * thus the error trap. */ meta_error_trap_push (window->display); + window->mapped = FALSE; /* Keep track of unmapping it, so we + * can identify a withdraw initiated + * by the client. + */ XReparentWindow (window->display->xdisplay, window->xwindow, window->screen->xroot, @@ -323,6 +328,12 @@ meta_window_destroy_frame (MetaWindow *window) XDestroyWindow (window->display->xdisplay, frame->xwindow); g_free (frame); + + /* Put our state back where it should be */ + if (window->iconic) + meta_window_hide (window); + else + meta_window_show (window); } void diff --git a/src/window.c b/src/window.c index c4ad7f0c7..32037f8f6 100644 --- a/src/window.c +++ b/src/window.c @@ -121,12 +121,16 @@ meta_window_new (MetaDisplay *display, Window xwindow) window->xvisual = attrs.visual; window->title = NULL; - window->iconic = FALSE; window->desc = g_strdup_printf ("0x%lx", window->xwindow); window->frame = NULL; window->has_focus = FALSE; + + window->initially_iconic = FALSE; + window->minimized = FALSE; + window->iconic = FALSE; + window->mapped = FALSE; meta_display_register_x_window (display, &window->xwindow, window); @@ -134,10 +138,24 @@ meta_window_new (MetaDisplay *display, Window xwindow) update_title (window); update_protocols (window); update_wm_hints (window); + + if (window->initially_iconic) + { + /* WM_HINTS said iconic */ + window->iconic = TRUE; + } meta_window_resize (window, window->size_hints.width, window->size_hints.height); meta_window_ensure_frame (window); + + /* Put our state where it should be (ensure_frame also did this + * for decorated windows, but should be harmless to do twice) + */ + if (window->iconic) + meta_window_hide (window); + else + meta_window_show (window); return window; } @@ -150,6 +168,15 @@ meta_window_free (MetaWindow *window) meta_display_unregister_x_window (window->display, window->xwindow); meta_window_destroy_frame (window); + + /* Put back anything we messed up */ + meta_error_trap_push (window->display); + if (window->border_width != 0) + XSetWindowBorderWidth (window->display->xdisplay, + window->xwindow, + window->border_width); + + meta_error_trap_pop (window->display); g_free (window->title); g_free (window->desc); @@ -163,7 +190,15 @@ meta_window_show (MetaWindow *window) XMapWindow (window->display->xdisplay, window->frame->xwindow); XMapWindow (window->display->xdisplay, window->xwindow); + /* These flags aren't always in sync, iconic + * is set only here in show/hide, mapped + * can be set in a couple other places where + * we map/unmap + */ + window->mapped = TRUE; window->iconic = FALSE; + + /* FIXME update WM_STATE */ } void @@ -173,7 +208,30 @@ meta_window_hide (MetaWindow *window) XUnmapWindow (window->display->xdisplay, window->frame->xwindow); XUnmapWindow (window->display->xdisplay, window->xwindow); + window->mapped = FALSE; window->iconic = TRUE; + + /* FIXME update WM_STATE */ +} + +void +meta_window_minimize (MetaWindow *window) +{ + if (!window->minimized) + { + window->minimized = TRUE; + meta_window_hide (window); + } +} + +void +meta_window_unminimize (MetaWindow *window) +{ + if (window->minimized) + { + window->minimized = FALSE; + meta_window_show (window); + } } void @@ -206,9 +264,20 @@ meta_window_delete (MetaWindow *window, Time timestamp) { meta_error_trap_push (window->display); - meta_window_send_icccm_message (window, - window->display->atom_wm_delete_window, - timestamp); + if (window->delete_window) + { + meta_verbose ("Deleting %s with delete_window request\n", + window->desc); + meta_window_send_icccm_message (window, + window->display->atom_wm_delete_window, + timestamp); + } + else + { + meta_verbose ("Deleting %s with explicit kill\n", + window->desc); + XKillClient (window->display->xdisplay, window->xwindow); + } meta_error_trap_pop (window->display); } @@ -319,6 +388,13 @@ process_property_notify (MetaWindow *window, { update_protocols (window); + if (window->frame) + meta_frame_queue_recalc (window->frame); + } + else if (event->atom == XA_WM_HINTS) + { + update_wm_hints (window); + if (window->frame) meta_frame_queue_recalc (window->frame); } @@ -698,7 +774,7 @@ update_protocols (MetaWindow *window) if (XGetWMProtocols (window->display->xdisplay, window->xwindow, &protocols, - &n_protocols) == Success) + &n_protocols)) { i = 0; while (i < n_protocols) @@ -727,6 +803,7 @@ update_wm_hints (MetaWindow *window) /* Fill in defaults */ window->input = FALSE; + window->initially_iconic = FALSE; meta_error_trap_push (window->display); @@ -735,9 +812,14 @@ update_wm_hints (MetaWindow *window) if (hints) { window->input = (hints->flags & InputHint) != 0; + + window->initially_iconic = (hints->initial_state == IconicState); /* FIXME there are a few others there. */ + meta_verbose ("Read WM_HINTS input: %d iconic: %d\n", + window->input, window->initially_iconic); + XFree (hints); } diff --git a/src/window.h b/src/window.h index 2d258cdf6..453318dc3 100644 --- a/src/window.h +++ b/src/window.h @@ -37,8 +37,25 @@ struct _MetaWindow Visual *xvisual; char *desc; /* used in debug spew */ char *title; - + + /* Mapped is what we think the mapped state should be; + * so if we get UnmapNotify and mapped == TRUE then + * it's a withdraw, if mapped == FALSE the UnmapNotify + * is caused by us. + */ + guint mapped : 1; + /* Minimize is the state controlled by the minimize button */ + guint minimized : 1; + /* Iconic is the state in WM_STATE; happens for workspaces/shading + * in addition to minimize + */ guint iconic : 1; + /* initially_iconic is the WM_HINTS setting when we first manage + * the window. + */ + guint initially_iconic : 1; + + /* These are the two flags from WM_PROTOCOLS */ guint take_focus : 1; guint delete_window : 1; /* Globally active / No input */ @@ -58,11 +75,13 @@ struct _MetaWindow XSizeHints size_hints; }; -MetaWindow* meta_window_new (MetaDisplay *display, - Window xwindow); -void meta_window_free (MetaWindow *window); -void meta_window_show (MetaWindow *window); -void meta_window_hide (MetaWindow *window); +MetaWindow* meta_window_new (MetaDisplay *display, + Window xwindow); +void meta_window_free (MetaWindow *window); +void meta_window_show (MetaWindow *window); +void meta_window_hide (MetaWindow *window); +void meta_window_minimize (MetaWindow *window); +void meta_window_unminimize (MetaWindow *window); void meta_window_resize (MetaWindow *window, int w, int h);