diff --git a/src/common.h b/src/common.h index 8ac4ea890..10f6bd17e 100644 --- a/src/common.h +++ b/src/common.h @@ -24,6 +24,7 @@ /* Don't include GTK or core headers here */ #include +#include typedef enum { @@ -39,4 +40,26 @@ typedef enum META_FRAME_MAXIMIZED = 1 << 9 } MetaFrameFlags; +typedef enum +{ + META_MENU_OP_DELETE = 1 << 0, + META_MENU_OP_MINIMIZE = 1 << 1, + META_MENU_OP_UNMAXIMIZE = 1 << 2, + META_MENU_OP_MAXIMIZE = 1 << 3, + META_MENU_OP_UNSHADE = 1 << 4, + META_MENU_OP_SHADE = 1 << 5, + META_MENU_OP_UNSTICK = 1 << 6, + META_MENU_OP_STICK = 1 << 7, + META_MENU_OP_WORKSPACES = 1 << 8 +} MetaMenuOp; + +typedef struct _MetaWindowMenu MetaWindowMenu; + +typedef void (* MetaWindowMenuFunc) (MetaWindowMenu *menu, + Display *xdisplay, + Window client_xwindow, + MetaMenuOp op, + int workspace, + gpointer data); + #endif diff --git a/src/core.c b/src/core.c index 330dfefe2..c1b856af1 100644 --- a/src/core.c +++ b/src/core.c @@ -355,4 +355,24 @@ meta_core_get_frame_workspace (Display *xdisplay, return meta_window_get_net_wm_desktop (window); } +void +meta_core_show_window_menu (Display *xdisplay, + Window frame_xwindow, + int root_x, + int root_y, + int button, + Time timestamp) +{ + MetaDisplay *display; + MetaWindow *window; + + display = meta_display_for_x_display (xdisplay); + window = meta_display_lookup_x_window (display, frame_xwindow); + + if (window == NULL || window->frame == NULL) + meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); + + meta_window_show_menu (window, root_x, root_y, button, timestamp); +} + diff --git a/src/core.h b/src/core.h index bf6bcdcab..e6c3bcc67 100644 --- a/src/core.h +++ b/src/core.h @@ -91,6 +91,13 @@ int meta_core_get_frame_workspace (Display *xdisplay, Window frame_xwindow); +void meta_core_show_window_menu (Display *xdisplay, + Window frame_xwindow, + int root_x, + int root_y, + int button, + Time timestamp); + #endif diff --git a/src/display.c b/src/display.c index bea957918..652260835 100644 --- a/src/display.c +++ b/src/display.c @@ -125,7 +125,9 @@ meta_display_open (const char *name) "_NET_WM_STATE_SKIP_TASKBAR", "_NET_WM_STATE_SKIP_PAGER", "_WIN_WORKSPACE", - "_WIN_LAYER" + "_WIN_LAYER", + "_WIN_PROTOCOLS", + "_WIN_SUPPORTING_WM_CHECK" }; Atom atoms[G_N_ELEMENTS(atom_names)]; @@ -200,6 +202,8 @@ meta_display_open (const char *name) display->atom_net_wm_state_skip_pager = atoms[31]; display->atom_win_workspace = atoms[32]; display->atom_win_layer = atoms[33]; + display->atom_win_protocols = atoms[34]; + display->atom_win_supporting_wm_check = atoms[35]; /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, * created in screen_new @@ -268,7 +272,7 @@ meta_display_open (const char *name) set_string_hint (display, display->leader_window, - display->atom_net_supporting_wm_check, + display->atom_net_wm_name, "Metacity"); /* Now manage all existing windows */ diff --git a/src/display.h b/src/display.h index 6f2b24df5..e5fa6d5f6 100644 --- a/src/display.h +++ b/src/display.h @@ -90,6 +90,8 @@ struct _MetaDisplay Atom atom_net_wm_state_skip_pager; Atom atom_win_workspace; Atom atom_win_layer; + Atom atom_win_protocols; + Atom atom_win_supporting_wm_check; /* This is the actual window from focus events, * not the one we last set diff --git a/src/frame.c b/src/frame.c index 5f2b3f828..90df8c81a 100644 --- a/src/frame.c +++ b/src/frame.c @@ -27,7 +27,8 @@ ExposureMask | \ ButtonPressMask | ButtonReleaseMask | \ PointerMotionMask | PointerMotionHintMask | \ - EnterWindowMask | LeaveWindowMask) + EnterWindowMask | LeaveWindowMask | \ + FocusChangeMask) void meta_window_ensure_frame (MetaWindow *window) @@ -106,6 +107,9 @@ meta_window_ensure_frame (MetaWindow *window) meta_ui_set_frame_title (window->screen->ui, window->frame->xwindow, window->title); + + /* Move keybindings to frame instead of window */ + meta_window_grab_keys (window); } void @@ -117,7 +121,7 @@ meta_window_destroy_frame (MetaWindow *window) return; frame = window->frame; - + meta_ui_remove_frame (window->screen->ui, frame->xwindow); /* Unparent the client window; it may be destroyed, @@ -142,6 +146,9 @@ meta_window_destroy_frame (MetaWindow *window) window->frame = NULL; + /* Move keybindings to window instead of frame */ + meta_window_grab_keys (window); + /* should we push an error trap? */ XDestroyWindow (window->display->xdisplay, frame->xwindow); @@ -270,6 +277,8 @@ meta_frame_event (MetaFrame *frame, switch (event->type) { case KeyPress: + meta_display_process_key_press (frame->window->display, + frame->window, event); break; case KeyRelease: break; @@ -290,8 +299,9 @@ meta_frame_event (MetaFrame *frame, case LeaveNotify: break; case FocusIn: - break; case FocusOut: + meta_window_notify_focus (frame->window, + event); break; case KeymapNotify: break; diff --git a/src/frames.c b/src/frames.c index 1b0f37e04..235406f9a 100644 --- a/src/frames.c +++ b/src/frames.c @@ -300,9 +300,6 @@ meta_frames_destroy (GtkObject *object) MetaFrames *frames; frames = META_FRAMES (object); - - if (frames->menu) - gtk_widget_destroy (frames->menu); winlist = NULL; g_hash_table_foreach (frames->frames, @@ -856,13 +853,7 @@ meta_frames_end_grab (MetaFrames *frames, guint32 timestamp) { if (frames->grab_frame) - { - if (frames->grab_status == META_FRAME_STATUS_CLICKING_MENU) - { - if (frames->menu) - gtk_widget_destroy (frames->menu); - } - + { frames->grab_frame = NULL; frames->grab_status = META_FRAME_STATUS_NORMAL; gdk_pointer_ungrab (timestamp); @@ -1030,11 +1021,12 @@ meta_frames_button_press_event (GtkWidget *widget, if (status == META_FRAME_STATUS_CLICKING_MENU) { - meta_window_menu_show (frames, frame, - event->x_root, - event->y_root, - event->button, - event->time); + meta_core_show_window_menu (gdk_display, + frame->xwindow, + event->x_root, + event->y_root, + event->button, + event->time); } } else if (control == META_FRAME_CONTROL_RESIZE_SE && diff --git a/src/frames.h b/src/frames.h index d79c3057d..fe6bcc4af 100644 --- a/src/frames.h +++ b/src/frames.h @@ -89,8 +89,6 @@ struct _MetaFrames int text_height; GHashTable *frames; - - GtkWidget *menu; /* The below is all for grabs */ MetaFrameStatus grab_status; @@ -136,6 +134,7 @@ void meta_frames_get_pixmap_for_control (MetaFrames *frames, MetaFrameControl control, GdkPixmap **pixmap, GdkBitmap **mask); + void meta_frames_notify_menu_hide (MetaFrames *frames); #endif diff --git a/src/keybindings.c b/src/keybindings.c index 38ad32e10..009c3bc2d 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -22,6 +22,8 @@ #include "keybindings.h" #include "workspace.h" #include "errors.h" +#include "ui.h" +#include "frame.h" #include @@ -30,12 +32,18 @@ */ typedef void (* MetaKeyHandler) (MetaDisplay *display, + MetaWindow *window, XEvent *event, gpointer data); static void handle_activate_workspace (MetaDisplay *display, - XEvent *event, - gpointer data); + MetaWindow *window, + XEvent *event, + gpointer data); +static void handle_activate_menu (MetaDisplay *display, + MetaWindow *window, + XEvent *event, + gpointer data); typedef struct _MetaKeyBinding MetaKeyBinding; @@ -50,7 +58,7 @@ struct _MetaKeyBinding #define INTERESTING_MODIFIERS (ShiftMask | ControlMask | Mod1Mask) -static MetaKeyBinding bindings[] = { +static MetaKeyBinding screen_bindings[] = { { XK_F1, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (0), 0 }, { XK_F2, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (1), 0 }, { XK_F3, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (2), 0 }, @@ -62,41 +70,57 @@ static MetaKeyBinding bindings[] = { { XK_3, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (2), 0 }, { XK_4, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (3), 0 }, { XK_5, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (4), 0 }, - { XK_6, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (5), 0 } + { XK_6, Mod1Mask, handle_activate_workspace, GINT_TO_POINTER (5), 0 }, + { None, 0, NULL, NULL, 0 } }; -void -meta_display_init_keys (MetaDisplay *display) +static MetaKeyBinding window_bindings[] = { + { XK_space, Mod1Mask, handle_activate_menu, NULL, 0 }, + { None, 0, NULL, NULL, 0 } +}; + +static void +init_bindings (MetaDisplay *display, + MetaKeyBinding *bindings) { int i; i = 0; - while (i < G_N_ELEMENTS (bindings)) + while (bindings[i].keysym != None) { bindings[i].keycode = XKeysymToKeycode (display->xdisplay, bindings[i].keysym); ++i; } -} +} void -meta_screen_grab_keys (MetaScreen *screen) +meta_display_init_keys (MetaDisplay *display) +{ + init_bindings (display, screen_bindings); + init_bindings (display, window_bindings); +} + +static void +grab_keys (MetaKeyBinding *bindings, + MetaDisplay *display, + Window xwindow) { int i; i = 0; - while (i < G_N_ELEMENTS (bindings)) + while (bindings[i].keysym != None) { if (bindings[i].keycode != 0) { int result; - meta_error_trap_push (screen->display); - XGrabKey (screen->display->xdisplay, bindings[i].keycode, - bindings[i].mask, screen->xroot, True, + meta_error_trap_push (display); + XGrabKey (display->xdisplay, bindings[i].keycode, + bindings[i].mask, xwindow, True, GrabModeAsync, GrabModeAsync); - result = meta_error_trap_pop (screen->display); + result = meta_error_trap_pop (display); if (result != Success) { const char *name; @@ -107,8 +131,6 @@ meta_screen_grab_keys (MetaScreen *screen) if (result == BadAccess) meta_warning (_("Some other program is already using the key %s as a binding\n"), name); - else - meta_bug ("Unexpected error setting up keybindings\n"); } } @@ -116,18 +138,22 @@ meta_screen_grab_keys (MetaScreen *screen) } } -void -meta_screen_ungrab_keys (MetaScreen *screen) +static void +ungrab_keys (MetaKeyBinding *bindings, + MetaDisplay *display, + Window xwindow) { int i; i = 0; - while (i < G_N_ELEMENTS (bindings)) + while (bindings[i].keysym != None) { if (bindings[i].keycode != 0) { - XUngrabKey (screen->display->xdisplay, bindings[i].keycode, - bindings[i].mask, screen->xroot); + meta_error_trap_push (display); + XUngrabKey (display->xdisplay, bindings[i].keycode, + bindings[i].mask, xwindow); + meta_error_trap_pop (display); } ++i; @@ -135,8 +161,59 @@ meta_screen_ungrab_keys (MetaScreen *screen) } void -meta_display_process_key_press (MetaDisplay *display, - XEvent *event) +meta_screen_grab_keys (MetaScreen *screen) +{ + grab_keys (screen_bindings, screen->display, screen->xroot); +} + +void +meta_screen_ungrab_keys (MetaScreen *screen) +{ + ungrab_keys (screen_bindings, screen->display, screen->xroot); +} + +void +meta_window_grab_keys (MetaWindow *window) +{ + if (window->keys_grabbed) + { + if (window->frame && !window->grab_on_frame) + ungrab_keys (window_bindings, window->display, + window->xwindow); + else if (window->frame == NULL && + window->grab_on_frame) + ; /* continue to regrab on client window */ + else + return; /* already all good */ + } + + grab_keys (window_bindings, window->display, + window->frame ? window->frame->xwindow : window->xwindow); + + window->keys_grabbed = TRUE; + window->grab_on_frame = window->frame != NULL; +} + +void +meta_window_ungrab_keys (MetaWindow *window) +{ + if (window->keys_grabbed) + { + if (window->grab_on_frame && + window->frame != NULL) + ungrab_keys (window_bindings, window->display, + window->frame->xwindow); + else if (!window->grab_on_frame) + ungrab_keys (window_bindings, window->display, + window->xwindow); + } +} + +static void +process_event (MetaKeyBinding *bindings, + MetaDisplay *display, + MetaWindow *window, + XEvent *event) { KeySym keysym; int i; @@ -144,22 +221,32 @@ meta_display_process_key_press (MetaDisplay *display, keysym = XKeycodeToKeysym (display->xdisplay, event->xkey.keycode, 0); i = 0; - while (i < G_N_ELEMENTS (bindings)) + while (bindings[i].keysym != None) { if (bindings[i].keysym == keysym && ((event->xkey.state & INTERESTING_MODIFIERS) == bindings[i].mask)) { - (* bindings[i].handler) (display, event, bindings[i].data); + (* bindings[i].handler) (display, window, event, bindings[i].data); break; } ++i; } } + +void +meta_display_process_key_press (MetaDisplay *display, + MetaWindow *window, + XEvent *event) +{ + process_event (screen_bindings, display, window, event); + process_event (window_bindings, display, window, event); +} static void handle_activate_workspace (MetaDisplay *display, + MetaWindow *window, XEvent *event, gpointer data) { @@ -180,3 +267,22 @@ handle_activate_workspace (MetaDisplay *display, } } +static void +handle_activate_menu (MetaDisplay *display, + MetaWindow *window, + XEvent *event, + gpointer data) +{ + if (display->focus_window) + { + int x, y; + + meta_window_get_position (display->focus_window, + &x, &y); + + meta_window_show_menu (display->focus_window, + x, y, + 0, + event->xkey.time); + } +} diff --git a/src/keybindings.h b/src/keybindings.h index 9f252f096..3fa610197 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -23,11 +23,15 @@ #define META_KEYBINDINGS_H #include "display.h" +#include "window.h" void meta_display_init_keys (MetaDisplay *display); void meta_screen_grab_keys (MetaScreen *screen); void meta_screen_ungrab_keys (MetaScreen *screen); +void meta_window_grab_keys (MetaWindow *window); +void meta_window_ungrab_keys (MetaWindow *window); void meta_display_process_key_press (MetaDisplay *display, + MetaWindow *window, XEvent *event); #endif diff --git a/src/menu.c b/src/menu.c index e6cb010a3..2fb67a936 100644 --- a/src/menu.c +++ b/src/menu.c @@ -27,19 +27,6 @@ typedef struct _MenuItem MenuItem; typedef struct _MenuData MenuData; -typedef enum -{ - META_MENU_OP_DELETE = 1 << 0, - META_MENU_OP_MINIMIZE = 1 << 1, - META_MENU_OP_UNMAXIMIZE = 1 << 2, - META_MENU_OP_MAXIMIZE = 1 << 3, - META_MENU_OP_UNSHADE = 1 << 4, - META_MENU_OP_SHADE = 1 << 5, - META_MENU_OP_UNSTICK = 1 << 6, - META_MENU_OP_STICK = 1 << 7, - META_MENU_OP_WORKSPACES = 1 << 8 -} MetaMenuOp; - struct _MenuItem { MetaMenuOp op; @@ -50,8 +37,7 @@ struct _MenuItem struct _MenuData { - MetaFrames *frames; - MetaUIFrame *frame; + MetaWindowMenu *menu; MetaMenuOp op; }; @@ -92,70 +78,64 @@ popup_position_func (GtkMenu *menu, } static void -menu_closed (GtkMenu *menu, +menu_closed (GtkMenu *widget, gpointer data) { - MetaFrames *frames; + MetaWindowMenu *menu; + + menu = data; - frames = META_FRAMES (data); - - meta_frames_notify_menu_hide (frames); - - gtk_widget_destroy (frames->menu); - frames->menu = NULL; + meta_frames_notify_menu_hide (menu->frames); + (* menu->func) (menu, gdk_display, + menu->client_xwindow, + 0, 0, + menu->data); + + /* menu may now be freed */ } -void -meta_window_menu_show (MetaFrames *frames, - MetaUIFrame *frame, - int root_x, - int root_y, - int button, - guint32 timestamp) +static void +activate_cb (GtkWidget *menuitem, gpointer data) +{ + MenuData *md; + + g_return_if_fail (GTK_IS_WIDGET (menuitem)); + + md = data; + + meta_frames_notify_menu_hide (md->menu->frames); + (* md->menu->func) (md->menu, gdk_display, + md->menu->client_xwindow, + md->op, + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem), + "workspace")), + md->menu->data); + + /* menu may now be freed */ +} + +MetaWindowMenu* +meta_window_menu_new (MetaFrames *frames, + MetaMenuOp ops, + MetaMenuOp insensitive, + Window client_xwindow, + int active_workspace, + int n_workspaces, + MetaWindowMenuFunc func, + gpointer data) { int i; - GdkPoint *pt; - int n_workspaces; - int current_workspace; - MetaMenuOp ops; - MetaMenuOp insensitive; - MetaFrameFlags flags; - - flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); - - ops = 0; - insensitive = 0; - - if (flags & META_FRAME_ALLOWS_MAXIMIZE) - { - if (flags & META_FRAME_MAXIMIZED) - ops |= META_MENU_OP_UNMAXIMIZE; - else - ops |= META_MENU_OP_MAXIMIZE; - } + MetaWindowMenu *menu; - if (flags & META_FRAME_SHADED) - ops |= META_MENU_OP_UNSHADE; - else - ops |= META_MENU_OP_SHADE; - - if (flags & META_FRAME_STUCK) - ops |= META_MENU_OP_UNSTICK; - else - ops |= META_MENU_OP_STICK; + menu = g_new (MetaWindowMenu, 1); + menu->frames = frames; + menu->client_xwindow = client_xwindow; + menu->func = func; + menu->data = data; + menu->ops = ops; + menu->insensitive = insensitive; - ops |= (META_MENU_OP_DELETE | META_MENU_OP_WORKSPACES | META_MENU_OP_MINIMIZE); - - if (!(flags & META_FRAME_ALLOWS_MINIMIZE)) - insensitive |= META_MENU_OP_MINIMIZE; - - if (!(flags & META_FRAME_ALLOWS_DELETE)) - insensitive |= META_MENU_OP_DELETE; - - if (frames->menu) - gtk_widget_destroy (frames->menu); - - frames->menu = gtk_menu_new (); + menu->menu = gtk_menu_new (); i = 0; while (i < G_N_ELEMENTS (menuitems)) @@ -234,8 +214,7 @@ meta_window_menu_show (MetaFrames *frames, md = g_new (MenuData, 1); - md->frames = frames; - md->frame = frame; + md->menu = menu; md->op = menuitems[i].op; gtk_signal_connect_full (GTK_OBJECT (mi), @@ -246,7 +225,7 @@ meta_window_menu_show (MetaFrames *frames, g_free, FALSE, FALSE); } - gtk_menu_shell_append (GTK_MENU_SHELL (frames->menu), + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi); gtk_widget_show (mi); @@ -256,12 +235,8 @@ meta_window_menu_show (MetaFrames *frames, if (ops & META_MENU_OP_WORKSPACES) { - n_workspaces = meta_core_get_num_workspaces (DefaultScreenOfDisplay (gdk_display)); - current_workspace = meta_core_get_frame_workspace (gdk_display, - frame->xwindow); - meta_warning ("Creating %d-workspace menu current %d\n", - n_workspaces, current_workspace); + n_workspaces, active_workspace); if (n_workspaces > 0) { @@ -273,7 +248,7 @@ meta_window_menu_show (MetaFrames *frames, char *label; MenuData *md; - if (flags & META_FRAME_STUCK) + if (ops & META_MENU_OP_UNSTICK) label = g_strdup_printf (_("Only on workspace _%d\n"), i + 1); else @@ -284,15 +259,14 @@ meta_window_menu_show (MetaFrames *frames, g_free (label); - if (!(flags & META_FRAME_STUCK) && - (current_workspace == i || + if (!(ops & META_MENU_OP_UNSTICK) && + (active_workspace == i || insensitive & META_MENU_OP_WORKSPACES)) gtk_widget_set_sensitive (mi, FALSE); md = g_new (MenuData, 1); - md->frames = frames; - md->frame = frame; + md->menu = menu; md->op = META_MENU_OP_WORKSPACES; g_object_set_data (G_OBJECT (mi), @@ -306,7 +280,7 @@ meta_window_menu_show (MetaFrames *frames, md, g_free, FALSE, FALSE); - gtk_menu_shell_append (GTK_MENU_SHELL (frames->menu), + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi); gtk_widget_show (mi); @@ -318,14 +292,26 @@ meta_window_menu_show (MetaFrames *frames, else meta_verbose ("not creating workspace menu\n"); - gtk_signal_connect (GTK_OBJECT (frames->menu), + gtk_signal_connect (GTK_OBJECT (menu->menu), "selection_done", GTK_SIGNAL_FUNC (menu_closed), - frames); + menu); + + return menu; +} + +void +meta_window_menu_popup (MetaWindowMenu *menu, + int root_x, + int root_y, + int button, + guint32 timestamp) +{ + GdkPoint *pt; pt = g_new (GdkPoint, 1); - g_object_set_data_full (G_OBJECT (frames->menu), + g_object_set_data_full (G_OBJECT (menu->menu), "destroy-point", pt, g_free); @@ -333,82 +319,19 @@ meta_window_menu_show (MetaFrames *frames, pt->x = root_x; pt->y = root_y; - gtk_menu_popup (GTK_MENU (frames->menu), + gtk_menu_popup (GTK_MENU (menu->menu), NULL, NULL, popup_position_func, pt, button, timestamp); - if (!GTK_MENU_SHELL (frames->menu)->have_xgrab) + if (!GTK_MENU_SHELL (menu->menu)->have_xgrab) meta_warning ("GtkMenu failed to grab the pointer\n"); } -static void -activate_cb (GtkWidget *menuitem, gpointer data) +void +meta_window_menu_free (MetaWindowMenu *menu) { - MenuData *md; - - g_return_if_fail (GTK_IS_WIDGET (menuitem)); - - md = data; - - switch (md->op) - { - case META_MENU_OP_DELETE: - meta_core_delete (gdk_display, - md->frame->xwindow, - gtk_get_current_event_time ()); - break; - - case META_MENU_OP_MINIMIZE: - meta_core_minimize (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_UNMAXIMIZE: - meta_core_unmaximize (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_MAXIMIZE: - meta_core_maximize (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_UNSHADE: - meta_core_unshade (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_SHADE: - meta_core_shade (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_WORKSPACES: - { - int workspace; - - workspace = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem), - "workspace")); - - meta_core_change_workspace (gdk_display, md->frame->xwindow, - workspace); - } - break; - - case META_MENU_OP_STICK: - meta_core_stick (gdk_display, - md->frame->xwindow); - break; - - case META_MENU_OP_UNSTICK: - meta_core_unstick (gdk_display, - md->frame->xwindow); - break; - - default: - meta_warning (G_STRLOC": Unknown window op\n"); - break; - } + gtk_widget_destroy (menu->menu); + g_free (menu); } diff --git a/src/menu.h b/src/menu.h index 30a096fc2..7fb5deef5 100644 --- a/src/menu.h +++ b/src/menu.h @@ -25,14 +25,31 @@ #include #include "frames.h" -void meta_window_menu_show (MetaFrames *frames, - MetaUIFrame *frame, - int root_x, - int root_y, - int button, - guint32 timestamp); - +struct _MetaWindowMenu +{ + MetaFrames *frames; + Window client_xwindow; + GtkWidget *menu; + MetaWindowMenuFunc func; + gpointer data; + MetaMenuOp ops; + MetaMenuOp insensitive; +}; +MetaWindowMenu* meta_window_menu_new (MetaFrames *frames, + MetaMenuOp ops, + MetaMenuOp insensitive, + Window client_xwindow, + int active_workspace, + int n_workspaces, + MetaWindowMenuFunc func, + gpointer data); +void meta_window_menu_popup (MetaWindowMenu *menu, + int root_x, + int root_y, + int button, + guint32 timestamp); +void meta_window_menu_free (MetaWindowMenu *menu); #endif diff --git a/src/screen.c b/src/screen.c index a9c00b6e6..966c47d4a 100644 --- a/src/screen.c +++ b/src/screen.c @@ -50,6 +50,13 @@ set_wm_check_hint (MetaScreen *screen) screen->display->atom_net_supporting_wm_check, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); + + /* Legacy GNOME hint */ + XChangeProperty (screen->display->xdisplay, screen->xroot, + screen->display->atom_win_supporting_wm_check, + XA_WINDOW, + 32, PropModeReplace, (guchar*) data, 1); + return Success; } @@ -57,6 +64,7 @@ static int set_supported_hint (MetaScreen *screen) { #define N_SUPPORTED 21 +#define N_WIN_SUPPORTED 1 Atom atoms[N_SUPPORTED]; atoms[0] = screen->display->atom_net_wm_name; @@ -86,6 +94,14 @@ set_supported_hint (MetaScreen *screen) XA_ATOM, 32, PropModeReplace, (guchar*) atoms, N_SUPPORTED); + /* Set legacy GNOME hints */ + atoms[0] = screen->display->atom_win_layer; + + XChangeProperty (screen->display->xdisplay, screen->xroot, + screen->display->atom_win_protocols, + XA_ATOM, + 32, PropModeReplace, (guchar*) atoms, N_WIN_SUPPORTED); + return Success; #undef N_SUPPORTED } diff --git a/src/session.h b/src/session.h index 13021df61..6cca5d4d2 100644 --- a/src/session.h +++ b/src/session.h @@ -41,6 +41,8 @@ struct _MetaSessionInfo MetaSessionInfo* meta_window_lookup_session_info (MetaWindow *window); +void meta_session_init (const char *previous_id); + #endif diff --git a/src/ui.c b/src/ui.c index 791676683..ff8d312b4 100644 --- a/src/ui.c +++ b/src/ui.c @@ -22,6 +22,7 @@ #include "ui.h" #include "frames.h" #include "util.h" +#include "menu.h" struct _MetaUI { @@ -197,4 +198,37 @@ meta_ui_set_frame_title (MetaUI *ui, meta_frames_set_title (ui->frames, xwindow, title); } +MetaWindowMenu* +meta_ui_window_menu_new (MetaUI *ui, + Window client_xwindow, + MetaMenuOp ops, + MetaMenuOp insensitive, + int active_workspace, + int n_workspaces, + MetaWindowMenuFunc func, + gpointer data) +{ + return meta_window_menu_new (ui->frames, + ops, insensitive, + client_xwindow, + active_workspace, + n_workspaces, + func, data); +} + +void +meta_ui_window_menu_popup (MetaWindowMenu *menu, + int root_x, + int root_y, + int button, + guint32 timestamp) +{ + meta_window_menu_popup (menu, root_x, root_y, button, timestamp); +} + +void +meta_ui_window_menu_free (MetaWindowMenu *menu) +{ + meta_window_menu_free (menu); +} diff --git a/src/ui.h b/src/ui.h index b30b2f3fa..9ec9bdeec 100644 --- a/src/ui.h +++ b/src/ui.h @@ -71,4 +71,21 @@ void meta_ui_set_frame_title (MetaUI *ui, Window xwindow, const char *title); +MetaWindowMenu* meta_ui_window_menu_new (MetaUI *ui, + Window client_xwindow, + MetaMenuOp ops, + MetaMenuOp insensitive, + int active_workspace, + int n_workspaces, + MetaWindowMenuFunc func, + gpointer data); +void meta_ui_window_menu_popup (MetaWindowMenu *menu, + int root_x, + int root_y, + int button, + guint32 timestamp); +void meta_ui_window_menu_free (MetaWindowMenu *menu); + + + #endif diff --git a/src/window.c b/src/window.c index 92bd88a19..0fc1c9e31 100644 --- a/src/window.c +++ b/src/window.c @@ -25,6 +25,8 @@ #include "errors.h" #include "workspace.h" #include "stack.h" +#include "keybindings.h" +#include "ui.h" #include @@ -210,6 +212,8 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->placed = window->mapped; window->unmanaging = FALSE; window->calc_showing_queued = FALSE; + window->keys_grabbed = FALSE; + window->grab_on_frame = FALSE; window->unmaps_pending = 0; @@ -259,16 +263,19 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->minimized = TRUE; meta_verbose ("Window %s asked to start out minimized\n", window->desc); } - + /* FIXME we have a tendency to set this then immediately * change it again. */ set_wm_state (window, window->iconic ? IconicState : NormalState); set_net_wm_state (window); - + + /* keys grab on client window if no frame */ if (window->decorated) meta_window_ensure_frame (window); + meta_window_grab_keys (window); + space = meta_display_get_workspace_by_screen_index (window->display, window->screen, @@ -321,6 +328,9 @@ meta_window_free (MetaWindow *window) meta_verbose ("Unmanaging 0x%lx\n", window->xwindow); window->unmanaging = TRUE; + + if (window->display->focus_window == window) + window->display->focus_window = NULL; meta_window_unqueue_calc_showing (window); @@ -343,12 +353,15 @@ meta_window_free (MetaWindow *window) /* FIXME restore original size if window has maximized */ - set_wm_state (window, WithdrawnState); - + set_wm_state (window, WithdrawnState); + + if (window->frame) + meta_window_destroy_frame (window); + + meta_window_ungrab_keys (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) @@ -698,9 +711,13 @@ meta_window_unmaximize (MetaWindow *window) void meta_window_shade (MetaWindow *window) { + meta_verbose ("Shading %s\n", window->desc); if (!window->shaded) { window->shaded = TRUE; + + meta_window_focus (window, CurrentTime); + meta_window_queue_move_resize (window); meta_window_queue_calc_showing (window); @@ -711,6 +728,7 @@ meta_window_shade (MetaWindow *window) void meta_window_unshade (MetaWindow *window) { + meta_verbose ("Unshading %s\n", window->desc); if (window->shaded) { window->shaded = FALSE; @@ -1153,24 +1171,41 @@ meta_window_focus (MetaWindow *window, meta_verbose ("Setting input focus to window %s, input: %d take_focus: %d\n", window->desc, window->input, window->take_focus); - meta_error_trap_push (window->display); - - if (window->input) + if (window->shaded && window->frame) { - XSetInputFocus (window->display->xdisplay, - window->xwindow, - RevertToPointerRoot, - timestamp); + /* This is so we can still use keyboard shortcuts + * and still draw the window as focused. + */ + if (window->frame) + { + meta_verbose ("Focusing frame of %s\n", window->desc); + XSetInputFocus (window->display->xdisplay, + window->frame->xwindow, + RevertToPointerRoot, + CurrentTime); + } } - - if (window->take_focus) + else { - meta_window_send_icccm_message (window, - window->display->atom_wm_take_focus, - timestamp); + meta_error_trap_push (window->display); + + if (window->input) + { + XSetInputFocus (window->display->xdisplay, + window->xwindow, + RevertToPointerRoot, + timestamp); + } + + if (window->take_focus) + { + meta_window_send_icccm_message (window, + window->display->atom_wm_take_focus, + timestamp); + } + + meta_error_trap_pop (window->display); } - - meta_error_trap_pop (window->display); } void @@ -1504,6 +1539,34 @@ meta_window_client_message (MetaWindow *window, return FALSE; } +gboolean +meta_window_notify_focus (MetaWindow *window, + XEvent *event) +{ + /* note the event can be on either the window or the frame, + * we focus the frame for shaded windows + */ + + if (event->type == FocusIn) + { + if (window != window->display->focus_window) + window->display->focus_window = window; + window->has_focus = TRUE; + if (window->frame) + meta_frame_queue_draw (window->frame); + } + else if (event->type == FocusOut) + { + if (window == window->display->focus_window) + window->display->focus_window = NULL; + window->has_focus = FALSE; + if (window->frame) + meta_frame_queue_draw (window->frame); + } + + return FALSE; +} + static gboolean process_property_notify (MetaWindow *window, XPropertyEvent *event) @@ -2797,3 +2860,144 @@ constrain_position (MetaWindow *window, *new_x = x; *new_y = y; } + +static void +menu_callback (MetaWindowMenu *menu, + Display *xdisplay, + Window client_xwindow, + MetaMenuOp op, + int workspace_index, + gpointer data) +{ + MetaDisplay *display; + MetaWindow *window; + + display = meta_display_for_x_display (xdisplay); + window = meta_display_lookup_x_window (display, client_xwindow); + + if (window != NULL) /* window can be NULL */ + { + meta_verbose ("Menu op %d on %s\n", op, window->desc); + + /* op can be 0 for none */ + switch (op) + { + case META_MENU_OP_DELETE: + meta_window_delete (window, CurrentTime); + break; + + case META_MENU_OP_MINIMIZE: + meta_window_minimize (window); + break; + + case META_MENU_OP_UNMAXIMIZE: + meta_window_unmaximize (window); + break; + + case META_MENU_OP_MAXIMIZE: + meta_window_maximize (window); + break; + + case META_MENU_OP_UNSHADE: + meta_window_unshade (window); + break; + + case META_MENU_OP_SHADE: + meta_window_shade (window); + break; + + case META_MENU_OP_WORKSPACES: + { + MetaWorkspace *workspace; + + workspace = + meta_display_get_workspace_by_screen_index (window->display, + window->screen, + workspace_index); + + if (workspace) + meta_window_change_workspace (window, + workspace); + else + meta_warning ("Workspace %d doesn't exist\n", workspace_index); + } + break; + + case META_MENU_OP_STICK: + meta_window_stick (window); + break; + + case META_MENU_OP_UNSTICK: + meta_window_unstick (window); + break; + + case 0: + /* nothing */ + break; + + default: + meta_warning (G_STRLOC": Unknown window op\n"); + break; + } + } + else + { + meta_verbose ("Menu callback on nonexistent window\n"); + } + + meta_ui_window_menu_free (menu); +} + +void +meta_window_show_menu (MetaWindow *window, + int root_x, + int root_y, + int button, + Time timestamp) +{ + MetaMenuOp ops; + MetaMenuOp insensitive; + MetaWindowMenu *menu; + + ops = 0; + insensitive = 0; + + ops |= (META_MENU_OP_DELETE | META_MENU_OP_WORKSPACES | META_MENU_OP_MINIMIZE); + + if (window->maximized) + ops |= META_MENU_OP_UNMAXIMIZE; + else + ops |= META_MENU_OP_MAXIMIZE; + + if (!window->has_maximize_func) + insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE; + + if (window->shaded) + ops |= META_MENU_OP_UNSHADE; + else + ops |= META_MENU_OP_SHADE; + + if (window->on_all_workspaces) + ops |= META_MENU_OP_UNSTICK; + else + ops |= META_MENU_OP_STICK; + + if (!window->has_minimize_func) + insensitive |= META_MENU_OP_MINIMIZE; + + if (!window->has_close_func) + insensitive |= META_MENU_OP_DELETE; + + menu = + meta_ui_window_menu_new (window->screen->ui, + window->xwindow, + ops, + insensitive, + meta_window_get_net_wm_desktop (window), + meta_screen_get_n_workspaces (window->screen), + menu_callback, + NULL); + + meta_verbose ("Popping up window menu for %s\n", window->desc); + meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp); +} diff --git a/src/window.h b/src/window.h index 98f50ece4..d6351d47d 100644 --- a/src/window.h +++ b/src/window.h @@ -139,6 +139,10 @@ struct _MetaWindow /* Are we in the calc_showing queue? */ guint calc_showing_queued : 1; + + /* Used by keybindings.c */ + guint keys_grabbed : 1; + guint grab_on_frame : 1; /* Number of UnmapNotify that are caused by us, if * we get UnmapNotify with none pending then the client @@ -227,8 +231,17 @@ gboolean meta_window_property_notify (MetaWindow *window, XEvent *event); gboolean meta_window_client_message (MetaWindow *window, XEvent *event); +gboolean meta_window_notify_focus (MetaWindow *window, + XEvent *event); int meta_window_set_current_workspace_hint (MetaWindow *window); unsigned long meta_window_get_net_wm_desktop (MetaWindow *window); + +void meta_window_show_menu (MetaWindow *window, + int root_x, + int root_y, + int button, + Time timestamp); + #endif