/* Metacity X display handler */ /* * Copyright (C) 2001 Havoc Pennington * * 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 #include "display.h" #include "util.h" #include "main.h" #include "screen.h" #include "window.h" #include "frame.h" #include "errors.h" #include "keybindings.h" #include "prefs.h" #include "resizepopup.h" #include "workspace.h" #include #include #include #define USE_GDK_DISPLAY typedef struct { MetaDisplay *display; Window xwindow; Time timestamp; MetaWindowPingFunc ping_reply_func; MetaWindowPingFunc ping_timeout_func; void *user_data; guint ping_timeout_id; } MetaPingData; static GSList *all_displays = NULL; static void meta_spew_event (MetaDisplay *display, XEvent *event); static void event_queue_callback (XEvent *event, gpointer data); static gboolean event_callback (XEvent *event, gpointer data); static Window event_get_modified_window (MetaDisplay *display, XEvent *event); static guint32 event_get_time (MetaDisplay *display, XEvent *event); static void process_pong_message (MetaDisplay *display, XEvent *event); static gint unsigned_long_equal (gconstpointer v1, gconstpointer v2) { return *((const gulong*) v1) == *((const gulong*) v2); } static guint unsigned_long_hash (gconstpointer v) { gulong val = * (const gulong *) v; /* I'm not sure this works so well. */ #if G_SIZEOF_LONG > 4 return (guint) (val ^ (val >> 32)); #else return val; #endif } static int set_utf8_string_hint (MetaDisplay *display, Window xwindow, Atom atom, const char *val) { meta_error_trap_push (display); XChangeProperty (display->xdisplay, xwindow, atom, display->atom_utf8_string, 8, PropModeReplace, (guchar*) val, strlen (val) + 1); return meta_error_trap_pop (display); } static void ping_data_free (MetaPingData *ping_data) { /* Remove the timeout */ if (ping_data->ping_timeout_id != 0) g_source_remove (ping_data->ping_timeout_id); g_free (ping_data); } static void remove_pending_pings_for_window (MetaDisplay *display, Window xwindow) { GSList *tmp; GSList *dead; /* could obviously be more efficient, don't care */ /* build list to be removed */ dead = NULL; for (tmp = display->pending_pings; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; if (ping_data->xwindow == xwindow) dead = g_slist_prepend (dead, ping_data); } /* remove what we found */ for (tmp = dead; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; display->pending_pings = g_slist_remove (display->pending_pings, ping_data); ping_data_free (ping_data); } g_slist_free (dead); } gboolean meta_display_open (const char *name) { MetaDisplay *display; Display *xdisplay; GSList *screens; GSList *tmp; int i; /* 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", "WM_STATE", "_NET_CLOSE_WINDOW", "_NET_WM_STATE", "_MOTIF_WM_HINTS", "_NET_WM_STATE_SHADED", "_NET_WM_STATE_MAXIMIZED_HORZ", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_DESKTOP", "_NET_NUMBER_OF_DESKTOPS", "WM_CHANGE_STATE", "SM_CLIENT_ID", "WM_CLIENT_LEADER", "WM_WINDOW_ROLE", "_NET_CURRENT_DESKTOP", "_NET_SUPPORTING_WM_CHECK", "_NET_SUPPORTED", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_TOOLBAR", "_NET_WM_WINDOW_TYPE_MENU", "_NET_WM_WINDOW_TYPE_DIALOG", "_NET_WM_WINDOW_TYPE_NORMAL", "_NET_WM_STATE_MODAL", "_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING", "_NET_WM_STATE_SKIP_TASKBAR", "_NET_WM_STATE_SKIP_PAGER", "_WIN_WORKSPACE", "_WIN_LAYER", "_WIN_PROTOCOLS", "_WIN_SUPPORTING_WM_CHECK", "_NET_WM_ICON_NAME", "_NET_WM_ICON", "_NET_WM_ICON_GEOMETRY", "UTF8_STRING", "WM_ICON_SIZE", "_KWM_WIN_ICON", "_NET_WM_MOVERESIZE", "_NET_ACTIVE_WINDOW", "_METACITY_RESTART_MESSAGE", "_NET_WM_STRUT", "_WIN_HINTS", "_METACITY_RELOAD_THEME_MESSAGE", "_METACITY_SET_KEYBINDINGS_MESSAGE", "_NET_WM_STATE_HIDDEN", "_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WM_WINDOW_TYPE_SPLASHSCREEN", "_NET_WM_STATE_FULLSCREEN", "_NET_WM_PING", "_NET_WM_PID", "WM_CLIENT_MACHINE", "_NET_WM_WORKAREA" }; Atom atoms[G_N_ELEMENTS(atom_names)]; meta_verbose ("Opening display '%s'\n", XDisplayName (name)); #ifdef USE_GDK_DISPLAY xdisplay = meta_ui_get_display (name); #else xdisplay = XOpenDisplay (name); #endif if (xdisplay == NULL) { meta_warning (_("Failed to open X Window System display '%s'\n"), XDisplayName (name)); return FALSE; } if (meta_is_syncing ()) XSynchronize (xdisplay, True); display = g_new (MetaDisplay, 1); /* here we use XDisplayName which is what the user * probably put in, vs. DisplayString(display) which is * canonicalized by XOpenDisplay() */ display->name = g_strdup (XDisplayName (name)); display->xdisplay = xdisplay; display->error_traps = 0; display->error_trap_handler = NULL; display->server_grab_count = 0; display->workspaces = NULL; display->pending_pings = NULL; display->focus_window = NULL; display->mru_list = NULL; display->showing_desktop = FALSE; /* FIXME copy the checks from GDK probably */ display->static_gravity_works = g_getenv ("METACITY_USE_STATIC_GRAVITY") != NULL; /* we have to go ahead and do this so error handlers work */ all_displays = g_slist_prepend (all_displays, display); meta_display_init_keys (display); 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]; display->atom_wm_state = atoms[4]; display->atom_net_close_window = atoms[5]; display->atom_net_wm_state = atoms[6]; display->atom_motif_wm_hints = atoms[7]; display->atom_net_wm_state_shaded = atoms[8]; display->atom_net_wm_state_maximized_horz = atoms[9]; display->atom_net_wm_state_maximized_vert = atoms[10]; display->atom_net_wm_desktop = atoms[11]; display->atom_net_number_of_desktops = atoms[12]; display->atom_wm_change_state = atoms[13]; display->atom_sm_client_id = atoms[14]; display->atom_wm_client_leader = atoms[15]; display->atom_wm_window_role = atoms[16]; display->atom_net_current_desktop = atoms[17]; display->atom_net_supporting_wm_check = atoms[18]; display->atom_net_supported = atoms[19]; display->atom_net_wm_window_type = atoms[20]; display->atom_net_wm_window_type_desktop = atoms[21]; display->atom_net_wm_window_type_dock = atoms[22]; display->atom_net_wm_window_type_toolbar = atoms[23]; display->atom_net_wm_window_type_menu = atoms[24]; display->atom_net_wm_window_type_dialog = atoms[25]; display->atom_net_wm_window_type_normal = atoms[26]; display->atom_net_wm_state_modal = atoms[27]; display->atom_net_client_list = atoms[28]; display->atom_net_client_list_stacking = atoms[29]; display->atom_net_wm_state_skip_taskbar = atoms[30]; 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]; display->atom_net_wm_icon_name = atoms[36]; display->atom_net_wm_icon = atoms[37]; display->atom_net_wm_icon_geometry = atoms[38]; display->atom_utf8_string = atoms[39]; display->atom_wm_icon_size = atoms[40]; display->atom_kwm_win_icon = atoms[41]; display->atom_net_wm_moveresize = atoms[42]; display->atom_net_active_window = atoms[43]; display->atom_metacity_restart_message = atoms[44]; display->atom_net_wm_strut = atoms[45]; display->atom_win_hints = atoms[46]; display->atom_metacity_reload_theme_message = atoms[47]; display->atom_metacity_set_keybindings_message = atoms[48]; display->atom_net_wm_state_hidden = atoms[49]; display->atom_net_wm_window_type_utility = atoms[50]; display->atom_net_wm_window_type_splashscreen = atoms[51]; display->atom_net_wm_state_fullscreen = atoms[52]; display->atom_net_wm_ping = atoms[53]; display->atom_net_wm_pid = atoms[54]; display->atom_wm_client_machine = atoms[55]; display->atom_net_wm_workarea = atoms[56]; /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, * created in screen_new */ display->leader_window = None; display->no_focus_window = None; screens = NULL; #if 0 /* disable multihead pending GTK support */ i = 0; while (i < ScreenCount (xdisplay)) { MetaScreen *screen; screen = meta_screen_new (display, i); if (screen) screens = g_slist_prepend (screens, screen); ++i; } #else { MetaScreen *screen; screen = meta_screen_new (display, DefaultScreen (xdisplay)); if (screen) screens = g_slist_prepend (screens, screen); } #endif if (screens == NULL) { /* This would typically happen because all the screens already * have window managers */ #ifndef USE_GDK_DISPLAY XCloseDisplay (xdisplay); #endif all_displays = g_slist_remove (all_displays, display); g_free (display->name); g_free (display); return FALSE; } display->screens = screens; #ifdef USE_GDK_DISPLAY display->events = NULL; /* Get events */ meta_ui_add_event_func (display->xdisplay, event_callback, display); #else display->events = meta_event_queue_new (display->xdisplay, event_queue_callback, display); #endif display->window_ids = g_hash_table_new (unsigned_long_hash, unsigned_long_equal); display->double_click_time = 250; display->last_button_time = 0; display->last_button_xwindow = None; display->last_button_num = 0; display->is_double_click = FALSE; i = 0; while (i < N_IGNORED_SERIALS) { display->ignored_serials[i] = 0; ++i; } display->ungrab_should_not_cause_focus_window = None; display->current_time = CurrentTime; display->grab_op = META_GRAB_OP_NONE; display->grab_window = NULL; display->grab_resize_popup = NULL; set_utf8_string_hint (display, display->leader_window, display->atom_net_wm_name, "Metacity"); { /* The legacy GNOME hint is to set a cardinal which is the window * id of the supporting_wm_check window on the supporting_wm_check * window itself */ gulong data[1]; data[0] = display->leader_window; XChangeProperty (display->xdisplay, display->leader_window, display->atom_win_supporting_wm_check, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); } /* Now manage all existing windows */ tmp = display->screens; while (tmp != NULL) { meta_screen_manage_all_windows (tmp->data); tmp = tmp->next; } return TRUE; } static void listify_func (gpointer key, gpointer value, gpointer data) { GSList **listp; listp = data; *listp = g_slist_prepend (*listp, value); } static gint ptrcmp (gconstpointer a, gconstpointer b) { if (a < b) return -1; else if (a > b) return 1; else return 0; } GSList* meta_display_list_windows (MetaDisplay *display) { GSList *winlist; GSList *tmp; GSList *prev; winlist = NULL; g_hash_table_foreach (display->window_ids, listify_func, &winlist); /* Uniquify the list, since both frame windows and plain * windows are in the hash */ winlist = g_slist_sort (winlist, ptrcmp); prev = NULL; tmp = winlist; while (tmp != NULL) { GSList *next; next = tmp->next; if (next && next->data == tmp->data) { /* Delete tmp from list */ if (prev) prev->next = next; if (tmp == winlist) winlist = next; g_slist_free_1 (tmp); /* leave prev unchanged */ } else { prev = tmp; } tmp = next; } return winlist; } void meta_display_close (MetaDisplay *display) { GSList *winlist; GSList *tmp; if (display->error_traps > 0) meta_bug ("Display closed with error traps pending\n"); winlist = meta_display_list_windows (display); /* Unmanage all windows */ meta_display_grab (display); tmp = winlist; while (tmp != NULL) { meta_window_free (tmp->data); tmp = tmp->next; } g_slist_free (winlist); meta_display_ungrab (display); #ifdef USE_GDK_DISPLAY /* Stop caring about events */ meta_ui_remove_event_func (display->xdisplay, event_callback, display); #endif /* Free all screens */ tmp = display->screens; while (tmp != NULL) { MetaScreen *screen = tmp->data; meta_screen_free (screen); tmp = tmp->next; } g_slist_free (display->screens); display->screens = NULL; /* Must be after all calls to meta_window_free() since they * unregister windows */ g_hash_table_destroy (display->window_ids); if (display->leader_window != None) XDestroyWindow (display->xdisplay, display->leader_window); #ifndef USE_GDK_DISPLAY meta_event_queue_free (display->events); XCloseDisplay (display->xdisplay); #endif g_free (display->name); all_displays = g_slist_remove (all_displays, display); meta_display_shutdown_keys (display); g_free (display); if (all_displays == NULL) { meta_verbose ("Last display closed, exiting\n"); meta_quit (META_EXIT_SUCCESS); } } MetaScreen* meta_display_screen_for_root (MetaDisplay *display, Window xroot) { GSList *tmp; tmp = display->screens; while (tmp != NULL) { MetaScreen *screen = tmp->data; if (xroot == screen->xroot) return screen; tmp = tmp->next; } return NULL; } MetaScreen* meta_display_screen_for_x_screen (MetaDisplay *display, Screen *xscreen) { GSList *tmp; tmp = display->screens; while (tmp != NULL) { MetaScreen *screen = tmp->data; if (xscreen == screen->xscreen) return screen; tmp = tmp->next; } return NULL; } /* Grab/ungrab routines taken from fvwm */ void meta_display_grab (MetaDisplay *display) { if (display->server_grab_count == 0) { XSync (display->xdisplay, False); XGrabServer (display->xdisplay); } XSync (display->xdisplay, False); display->server_grab_count += 1; meta_verbose ("Grabbing display, grab count now %d\n", display->server_grab_count); } void meta_display_ungrab (MetaDisplay *display) { if (display->server_grab_count == 0) meta_bug ("Ungrabbed non-grabbed server\n"); display->server_grab_count -= 1; if (display->server_grab_count == 0) { /* FIXME we want to purge all pending "queued" stuff * at this point, such as window hide/show */ XSync (display->xdisplay, False); XUngrabServer (display->xdisplay); } XSync (display->xdisplay, False); meta_verbose ("Ungrabbing display, grab count now %d\n", display->server_grab_count); } MetaDisplay* meta_display_for_x_display (Display *xdisplay) { GSList *tmp; tmp = all_displays; while (tmp != NULL) { MetaDisplay *display = tmp->data; if (display->xdisplay == xdisplay) return display; tmp = tmp->next; } meta_warning ("Could not find display for X display %p, probably going to crash\n", xdisplay); return NULL; } GSList* meta_displays_list (void) { return all_displays; } gboolean meta_display_is_double_click (MetaDisplay *display) { return display->is_double_click; } static gboolean dump_events = TRUE; static void event_queue_callback (XEvent *event, gpointer data) { event_callback (event, data); } static gboolean grab_op_is_mouse (MetaGrabOp op) { switch (op) { case META_GRAB_OP_MOVING: case META_GRAB_OP_RESIZING_SE: case META_GRAB_OP_RESIZING_S: case META_GRAB_OP_RESIZING_SW: case META_GRAB_OP_RESIZING_N: case META_GRAB_OP_RESIZING_NE: case META_GRAB_OP_RESIZING_NW: case META_GRAB_OP_RESIZING_W: case META_GRAB_OP_RESIZING_E: return TRUE; break; default: return FALSE; } } static gboolean grab_op_is_keyboard (MetaGrabOp op) { switch (op) { case META_GRAB_OP_KEYBOARD_MOVING: case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: case META_GRAB_OP_KEYBOARD_RESIZING_S: case META_GRAB_OP_KEYBOARD_RESIZING_N: case META_GRAB_OP_KEYBOARD_RESIZING_W: case META_GRAB_OP_KEYBOARD_RESIZING_E: case META_GRAB_OP_KEYBOARD_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_KEYBOARD_RESIZING_NW: case META_GRAB_OP_KEYBOARD_TABBING_NORMAL: case META_GRAB_OP_KEYBOARD_TABBING_DOCK: return TRUE; break; default: return FALSE; } } /* Get time of current event, or CurrentTime if none. */ guint32 meta_display_get_current_time (MetaDisplay *display) { return display->current_time; } static void add_ignored_serial (MetaDisplay *display, unsigned long serial) { int i; /* don't add the same serial more than once */ if (display->ignored_serials[N_IGNORED_SERIALS-1] == serial) return; /* shift serials to the left */ i = 0; while (i < (N_IGNORED_SERIALS - 1)) { display->ignored_serials[i] = display->ignored_serials[i+1]; ++i; } /* put new one on the end */ display->ignored_serials[i] = serial; } static gboolean serial_is_ignored (MetaDisplay *display, unsigned long serial) { int i; i = 0; while (i < N_IGNORED_SERIALS) { if (display->ignored_serials[i] == serial) return TRUE; ++i; } return FALSE; } static void reset_ignores (MetaDisplay *display) { int i; i = 0; while (i < N_IGNORED_SERIALS) { display->ignored_serials[i] = 0; ++i; } display->ungrab_should_not_cause_focus_window = None; } static gboolean event_callback (XEvent *event, gpointer data) { MetaWindow *window; MetaDisplay *display; Window modified; gboolean frame_was_receiver; gboolean filter_out_event; display = data; if (dump_events) meta_spew_event (display, event); filter_out_event = FALSE; display->current_time = event_get_time (display, event); modified = event_get_modified_window (display, event); if (event->type == ButtonPress) { /* filter out scrollwheel */ if (event->xbutton.button == 4 || event->xbutton.button == 5) return FALSE; /* mark double click events, kind of a hack, oh well. */ if (((int)event->xbutton.button) == display->last_button_num && event->xbutton.window == display->last_button_xwindow && event->xbutton.time < (display->last_button_time + display->double_click_time)) { display->is_double_click = TRUE; meta_topic (META_DEBUG_EVENTS, "This was the second click of a double click\n"); } else { display->is_double_click = FALSE; } display->last_button_num = event->xbutton.button; display->last_button_xwindow = event->xbutton.window; display->last_button_time = event->xbutton.time; } else if (event->type == UnmapNotify) { if (meta_ui_window_should_not_cause_focus (display->xdisplay, modified)) { add_ignored_serial (display, event->xany.serial); meta_topic (META_DEBUG_FOCUS, "Adding EnterNotify serial %lu to ignored focus serials\n", event->xany.serial); } } else if (event->type == LeaveNotify && event->xcrossing.mode == NotifyUngrab && modified == display->ungrab_should_not_cause_focus_window) { add_ignored_serial (display, event->xany.serial); meta_topic (META_DEBUG_FOCUS, "Adding LeaveNotify serial %lu to ignored focus serials\n", event->xany.serial); } if (modified != None) window = meta_display_lookup_x_window (display, modified); else window = NULL; frame_was_receiver = FALSE; if (window && window->frame && modified == window->frame->xwindow) frame_was_receiver = TRUE; switch (event->type) { case KeyPress: case KeyRelease: meta_display_process_key_event (display, window, event); break; case ButtonPress: if ((grab_op_is_mouse (display->grab_op) && display->grab_button != (int) event->xbutton.button && display->grab_window == window) || grab_op_is_keyboard (display->grab_op)) { meta_verbose ("Ending grab op %d on window %s due to button press\n", display->grab_op, display->grab_window->desc); meta_display_end_grab_op (display, event->xbutton.time); } else if (window && display->grab_op == META_GRAB_OP_NONE) { gboolean begin_move = FALSE; guint grab_mask; gboolean unmodified; grab_mask = Mod1Mask; if (g_getenv ("METACITY_DEBUG_BUTTON_GRABS")) grab_mask |= ControlMask; /* Two possible sources of an unmodified event; one is a * client that's letting button presses pass through to the * frame, the other is our focus_window_grab on unmodified * button 1. So for all such events we focus the window. */ unmodified = (event->xbutton.state & grab_mask) == 0; if (unmodified || event->xbutton.button == 1) { meta_window_raise (window); meta_topic (META_DEBUG_FOCUS, "Focusing %s due to unmodified button %d press (display.c)\n", window->desc, event->xbutton.button); meta_window_focus (window, event->xbutton.time); if (!frame_was_receiver && unmodified) { /* This is from our synchronous grab since * it has no modifiers and was on the client window */ int mode; /* When clicking a different app in click-to-focus * in application-based mode, and the different * app is not a dock or desktop, eat the focus click. */ if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK && meta_prefs_get_application_based () && !window->has_focus && window->type != META_WINDOW_DOCK && window->type != META_WINDOW_DESKTOP && (display->focus_window == NULL || !meta_window_same_application (window, display->focus_window))) mode = AsyncPointer; /* eat focus click */ else mode = ReplayPointer; /* give event back */ XAllowEvents (display->xdisplay, mode, event->xbutton.time); } /* you can move on alt-click but not on * the click-to-focus */ if (!unmodified) begin_move = TRUE; } else if (event->xbutton.button == 2) { begin_move = TRUE; } else if (event->xbutton.button == 3) { meta_window_show_menu (window, event->xbutton.x_root, event->xbutton.y_root, event->xbutton.button, event->xbutton.time); } if (begin_move && window->has_move_func) { meta_display_begin_grab_op (display, window, META_GRAB_OP_MOVING, TRUE, event->xbutton.button, 0, event->xbutton.time, event->xbutton.x_root, event->xbutton.y_root); } } break; case ButtonRelease: if (grab_op_is_mouse (display->grab_op) && display->grab_window == window) meta_window_handle_mouse_grab_op_event (window, event); break; case MotionNotify: if (grab_op_is_mouse (display->grab_op) && display->grab_window == window) meta_window_handle_mouse_grab_op_event (window, event); break; case EnterNotify: /* do this even if window->has_focus to avoid races */ if (window && !serial_is_ignored (display, event->xany.serial)) { switch (meta_prefs_get_focus_mode ()) { case META_FOCUS_MODE_SLOPPY: case META_FOCUS_MODE_MOUSE: if (window->type != META_WINDOW_DOCK && window->type != META_WINDOW_DESKTOP) { meta_topic (META_DEBUG_FOCUS, "Focusing %s due to enter notify with serial %lu\n", window->desc, event->xany.serial); meta_window_focus (window, event->xcrossing.time); /* stop ignoring stuff */ reset_ignores (display); } break; case META_FOCUS_MODE_CLICK: break; } if (window->type == META_WINDOW_DOCK) meta_window_raise (window); } break; case LeaveNotify: if (window) { switch (meta_prefs_get_focus_mode ()) { case META_FOCUS_MODE_MOUSE: /* This is kind of questionable; but we normally * set focus to RevertToPointerRoot, so I guess * leaving it on PointerRoot when nothing is focused * is probably right. Anyway, unfocus the * focused window. */ if (window->has_focus) { meta_verbose ("Unsetting focus from %s due to LeaveNotify\n", window->desc); XSetInputFocus (display->xdisplay, display->no_focus_window, RevertToPointerRoot, event->xcrossing.time); } break; case META_FOCUS_MODE_SLOPPY: case META_FOCUS_MODE_CLICK: break; } if (window->type == META_WINDOW_DOCK && event->xcrossing.mode != NotifyGrab && event->xcrossing.mode != NotifyUngrab) meta_window_lower (window); } break; case FocusIn: case FocusOut: if (window) { meta_window_notify_focus (window, event); } else if (event->xany.window == display->no_focus_window) { meta_topic (META_DEBUG_FOCUS, "Focus %s event received on no_focus_window 0x%lx " "mode %s detail %s\n", event->type == FocusIn ? "in" : event->type == FocusOut ? "out" : "???", event->xany.window, meta_event_mode_to_string (event->xfocus.mode), meta_event_detail_to_string (event->xfocus.mode)); } else if (meta_display_screen_for_root (display, event->xany.window) != NULL) { meta_topic (META_DEBUG_FOCUS, "Focus %s event received on root window 0x%lx " "mode %s detail %s\n", event->type == FocusIn ? "in" : event->type == FocusOut ? "out" : "???", event->xany.window, meta_event_mode_to_string (event->xfocus.mode), meta_event_detail_to_string (event->xfocus.mode)); } break; case KeymapNotify: break; case Expose: break; case GraphicsExpose: break; case NoExpose: break; case VisibilityNotify: break; case CreateNotify: break; case DestroyNotify: if (window) { if (display->grab_op != META_GRAB_OP_NONE && display->grab_window == window) meta_display_end_grab_op (display, CurrentTime); if (frame_was_receiver) { meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n", window->frame->xwindow); meta_error_trap_push (display); meta_window_destroy_frame (window->frame->window); meta_error_trap_pop (display); } else { meta_window_free (window); /* Unmanage destroyed window */ } } break; case UnmapNotify: if (display->grab_op != META_GRAB_OP_NONE && display->grab_window == window) meta_display_end_grab_op (display, CurrentTime); if (!frame_was_receiver && window) { if (window->unmaps_pending == 0) { meta_topic (META_DEBUG_WINDOW_STATE, "Window %s withdrawn\n", window->desc); window->withdrawn = TRUE; meta_window_free (window); /* Unmanage withdrawn window */ window = NULL; } else { window->unmaps_pending -= 1; meta_topic (META_DEBUG_WINDOW_STATE, "Received pending unmap, %d now pending\n", window->unmaps_pending); } } /* Unfocus on UnmapNotify, do this after the possible * window_free above so that window_free can see if window->has_focus * and move focus to another window */ if (window) meta_window_notify_focus (window, event); break; case MapNotify: break; case MapRequest: if (window == NULL) { window = meta_window_new (display, event->xmaprequest.window, FALSE); } /* if frame was receiver it's some malicious send event or something */ else if (!frame_was_receiver && window) { if (window->minimized) meta_window_unminimize (window); } break; case ReparentNotify: break; case ConfigureNotify: break; case ConfigureRequest: /* This comment and code is found in both twm and fvwm */ /* * According to the July 27, 1988 ICCCM draft, we should ignore size and * position fields in the WM_NORMAL_HINTS property when we map a window. * Instead, we'll read the current geometry. Therefore, we should respond * to configuration requests for windows which have never been mapped. */ if (window == NULL) { unsigned int xwcm; XWindowChanges xwc; xwcm = event->xconfigurerequest.value_mask & (CWX | CWY | CWWidth | CWHeight | CWBorderWidth); xwc.x = event->xconfigurerequest.x; xwc.y = event->xconfigurerequest.y; xwc.width = event->xconfigurerequest.width; xwc.height = event->xconfigurerequest.height; xwc.border_width = event->xconfigurerequest.border_width; meta_verbose ("Configuring withdrawn window to %d,%d %dx%d border %d (some values may not be in mask)\n", xwc.x, xwc.y, xwc.width, xwc.height, xwc.border_width); meta_error_trap_push (display); XConfigureWindow (display->xdisplay, event->xconfigurerequest.window, xwcm, &xwc); meta_error_trap_pop (display); } else { if (!frame_was_receiver) meta_window_configure_request (window, event); } break; case GravityNotify: break; case ResizeRequest: break; case CirculateNotify: break; case CirculateRequest: break; case PropertyNotify: if (window && !frame_was_receiver) meta_window_property_notify (window, event); break; case SelectionClear: break; case SelectionRequest: break; case SelectionNotify: break; case ColormapNotify: break; case ClientMessage: if (window) { if (!frame_was_receiver) meta_window_client_message (window, event); } else { MetaScreen *screen; screen = meta_display_screen_for_root (display, event->xclient.window); if (screen) { if (event->xclient.message_type == display->atom_net_current_desktop) { int space; MetaWorkspace *workspace; space = event->xclient.data.l[0]; meta_verbose ("Request to change current workspace to %d\n", space); workspace = meta_display_get_workspace_by_screen_index (display, screen, space); if (workspace) meta_workspace_activate (workspace); else meta_verbose ("Don't know about workspace %d\n", space); } else if (event->xclient.message_type == display->atom_net_number_of_desktops) { int num_spaces; num_spaces = event->xclient.data.l[0]; meta_verbose ("Request to set number of workspaces to %d\n", num_spaces); meta_prefs_set_num_workspaces (num_spaces); } else if (event->xclient.message_type == display->atom_metacity_restart_message) { meta_verbose ("Received restart request\n"); meta_restart (); } else if (event->xclient.message_type == display->atom_metacity_reload_theme_message) { meta_verbose ("Received reload theme request\n"); meta_ui_set_current_theme (meta_prefs_get_theme (), TRUE); meta_display_retheme_all (); } else if (event->xclient.message_type == display->atom_metacity_set_keybindings_message) { meta_verbose ("Received set keybindings request = %d\n", (int) event->xclient.data.l[0]); meta_set_keybindings_disabled (!event->xclient.data.l[0]); } else if (event->xclient.message_type == display->atom_wm_protocols) { meta_verbose ("Received WM_PROTOCOLS message\n"); if ((Atom)event->xclient.data.l[0] == display->atom_net_wm_ping) { process_pong_message (display, event); /* We don't want ping reply events going into * the GTK+ event loop because gtk+ will treat * them as ping requests and send more replies. */ filter_out_event = TRUE; } } } } break; case MappingNotify: /* Let XLib know that there is a new keyboard mapping. */ XRefreshKeyboardMapping (&event->xmapping); meta_display_process_mapping_event (display, event); break; default: break; } display->current_time = CurrentTime; return filter_out_event; } /* Return the window this has to do with, if any, rather * than the frame or root window that was selecting * for substructure */ static Window event_get_modified_window (MetaDisplay *display, XEvent *event) { switch (event->type) { case KeyPress: case KeyRelease: case ButtonPress: case ButtonRelease: case MotionNotify: case FocusIn: case FocusOut: case KeymapNotify: case Expose: case GraphicsExpose: case NoExpose: case VisibilityNotify: case ResizeRequest: case PropertyNotify: case SelectionClear: case SelectionRequest: case SelectionNotify: case ColormapNotify: case ClientMessage: case EnterNotify: case LeaveNotify: return event->xany.window; case CreateNotify: return event->xcreatewindow.window; case DestroyNotify: return event->xdestroywindow.window; case UnmapNotify: return event->xunmap.window; case MapNotify: return event->xmap.window; case MapRequest: return event->xmaprequest.window; case ReparentNotify: return event->xreparent.window; case ConfigureNotify: return event->xconfigure.window; case ConfigureRequest: return event->xconfigurerequest.window; case GravityNotify: return event->xgravity.window; case CirculateNotify: return event->xcirculate.window; case CirculateRequest: return event->xcirculaterequest.window; case MappingNotify: return None; default: return None; } } static guint32 event_get_time (MetaDisplay *display, XEvent *event) { switch (event->type) { case KeyPress: case KeyRelease: return event->xkey.time; case ButtonPress: case ButtonRelease: return event->xbutton.time; case MotionNotify: return event->xmotion.time; case PropertyNotify: return event->xproperty.time; case SelectionClear: case SelectionRequest: case SelectionNotify: return event->xselection.time; case EnterNotify: case LeaveNotify: return event->xcrossing.time; case FocusIn: case FocusOut: case KeymapNotify: case Expose: case GraphicsExpose: case NoExpose: case MapNotify: case UnmapNotify: case VisibilityNotify: case ResizeRequest: case ColormapNotify: case ClientMessage: case CreateNotify: case DestroyNotify: case MapRequest: case ReparentNotify: case ConfigureNotify: case ConfigureRequest: case GravityNotify: case CirculateNotify: case CirculateRequest: case MappingNotify: default: return CurrentTime; } } const char* meta_event_detail_to_string (int d) { const char *detail = "???"; switch (d) { /* We are an ancestor in the A<->B focus change relationship */ case NotifyAncestor: detail = "NotifyAncestor"; break; case NotifyDetailNone: detail = "NotifyDetailNone"; break; /* We are a descendant in the A<->B focus change relationship */ case NotifyInferior: detail = "NotifyInferior"; break; case NotifyNonlinear: detail = "NotifyNonlinear"; break; case NotifyNonlinearVirtual: detail = "NotifyNonlinearVirtual"; break; case NotifyPointer: detail = "NotifyPointer"; break; case NotifyPointerRoot: detail = "NotifyPointerRoot"; break; case NotifyVirtual: detail = "NotifyVirtual"; break; } return detail; } const char* meta_event_mode_to_string (int m) { const char *mode = "???"; switch (m) { case NotifyNormal: mode = "NotifyNormal"; break; case NotifyGrab: mode = "NotifyGrab"; break; case NotifyUngrab: mode = "NotifyUngrab"; break; /* not sure any X implementations are missing this, but * it seems to be absent from some docs. */ #ifdef NotifyWhileGrabbed case NotifyWhileGrabbed: mode = "NotifyWhileGrabbed"; break; #endif } return mode; } static const char* stack_mode_to_string (int mode) { switch (mode) { case Above: return "Above"; case Below: return "Below"; case TopIf: return "TopIf"; case BottomIf: return "BottomIf"; case Opposite: return "Opposite"; } return "Unknown"; } static char* key_event_description (Display *xdisplay, XEvent *event) { KeySym keysym; keysym = XKeycodeToKeysym (xdisplay, event->xkey.keycode, 0); return g_strdup_printf ("Key '%s' state 0x%x", XKeysymToString (keysym), event->xkey.state); } static void meta_spew_event (MetaDisplay *display, XEvent *event) { const char *name = NULL; char *extra = NULL; char *winname; MetaScreen *screen; /* filter overnumerous events */ if (event->type == Expose || event->type == MotionNotify || event->type == NoExpose) return; switch (event->type) { case KeyPress: name = "KeyPress"; extra = key_event_description (display->xdisplay, event); break; case KeyRelease: name = "KeyRelease"; extra = key_event_description (display->xdisplay, event); break; case ButtonPress: name = "ButtonPress"; break; case ButtonRelease: name = "ButtonRelease"; break; case MotionNotify: name = "MotionNotify"; extra = g_strdup_printf ("win: 0x%lx x: %d y: %d", event->xmotion.window, event->xmotion.x, event->xmotion.y); break; case EnterNotify: name = "EnterNotify"; extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %s detail: %s focus: %d", event->xcrossing.window, event->xcrossing.root, event->xcrossing.subwindow, meta_event_mode_to_string (event->xcrossing.mode), meta_event_detail_to_string (event->xcrossing.detail), event->xcrossing.focus); break; case LeaveNotify: name = "LeaveNotify"; extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %s detail: %s focus: %d", event->xcrossing.window, event->xcrossing.root, event->xcrossing.subwindow, meta_event_mode_to_string (event->xcrossing.mode), meta_event_detail_to_string (event->xcrossing.detail), event->xcrossing.focus); break; case FocusIn: name = "FocusIn"; extra = g_strdup_printf ("detail: %s mode: %s\n", meta_event_detail_to_string (event->xfocus.detail), meta_event_mode_to_string (event->xfocus.mode)); break; case FocusOut: name = "FocusOut"; extra = g_strdup_printf ("detail: %s mode: %s\n", meta_event_detail_to_string (event->xfocus.detail), meta_event_mode_to_string (event->xfocus.mode)); break; case KeymapNotify: name = "KeymapNotify"; break; case Expose: name = "Expose"; break; case GraphicsExpose: name = "GraphicsExpose"; break; case NoExpose: name = "NoExpose"; break; case VisibilityNotify: name = "VisibilityNotify"; break; case CreateNotify: name = "CreateNotify"; break; case DestroyNotify: name = "DestroyNotify"; break; case UnmapNotify: name = "UnmapNotify"; break; case MapNotify: name = "MapNotify"; break; case MapRequest: name = "MapRequest"; break; case ReparentNotify: name = "ReparentNotify"; break; case ConfigureNotify: name = "ConfigureNotify"; extra = g_strdup_printf ("x: %d y: %d w: %d h: %d above: 0x%lx", event->xconfigure.x, event->xconfigure.y, event->xconfigure.width, event->xconfigure.height, event->xconfigure.above); break; case ConfigureRequest: name = "ConfigureRequest"; extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx x: %d %sy: %d %sw: %d %sh: %d %sborder: %d %sabove: %lx %sstackmode: %s %s", event->xconfigurerequest.parent, event->xconfigurerequest.window, event->xconfigurerequest.x, event->xconfigurerequest.value_mask & CWX ? "" : "(unset) ", event->xconfigurerequest.y, event->xconfigurerequest.value_mask & CWY ? "" : "(unset) ", event->xconfigurerequest.width, event->xconfigurerequest.value_mask & CWWidth ? "" : "(unset) ", event->xconfigurerequest.height, event->xconfigurerequest.value_mask & CWHeight ? "" : "(unset) ", event->xconfigurerequest.border_width, event->xconfigurerequest.value_mask & CWBorderWidth ? "" : "(unset)", event->xconfigurerequest.above, event->xconfigurerequest.value_mask & CWSibling ? "" : "(unset)", stack_mode_to_string (event->xconfigurerequest.detail), event->xconfigurerequest.value_mask & CWStackMode ? "" : "(unset)"); break; case GravityNotify: name = "GravityNotify"; break; case ResizeRequest: name = "ResizeRequest"; extra = g_strdup_printf ("width = %d height = %d", event->xresizerequest.width, event->xresizerequest.height); break; case CirculateNotify: name = "CirculateNotify"; break; case CirculateRequest: name = "CirculateRequest"; break; case PropertyNotify: { char *str; const char *state; name = "PropertyNotify"; meta_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xproperty.atom); meta_error_trap_pop (display); if (event->xproperty.state == PropertyNewValue) state = "PropertyNewValue"; else if (event->xproperty.state == PropertyDelete) state = "PropertyDelete"; else state = "???"; extra = g_strdup_printf ("atom: %s state: %s", str ? str : "(unknown atom)", state); XFree (str); } break; case SelectionClear: name = "SelectionClear"; break; case SelectionRequest: name = "SelectionRequest"; break; case SelectionNotify: name = "SelectionNotify"; break; case ColormapNotify: name = "ColormapNotify"; break; case ClientMessage: { char *str; name = "ClientMessage"; meta_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xclient.message_type); meta_error_trap_pop (display); extra = g_strdup_printf ("type: %s format: %d\n", str ? str : "(unknown atom)", event->xclient.format); XFree (str); } break; case MappingNotify: name = "MappingNotify"; break; default: name = "(Unknown event)"; extra = g_strdup_printf ("type: %d", event->xany.type); break; } screen = meta_display_screen_for_root (display, event->xany.window); if (screen) winname = g_strdup_printf ("root %d", screen->number); else winname = g_strdup_printf ("0x%lx", event->xany.window); meta_topic (META_DEBUG_EVENTS, "%s on %s%s %s %sserial %lu\n", name, winname, extra ? ":" : "", extra ? extra : "", event->xany.send_event ? "SEND " : "", event->xany.serial); g_free (winname); if (extra) g_free (extra); } MetaWindow* meta_display_lookup_x_window (MetaDisplay *display, Window xwindow) { return g_hash_table_lookup (display->window_ids, &xwindow); } void meta_display_register_x_window (MetaDisplay *display, Window *xwindowp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (display->window_ids, xwindowp) == NULL); g_hash_table_insert (display->window_ids, xwindowp, window); } void meta_display_unregister_x_window (MetaDisplay *display, Window xwindow) { g_return_if_fail (g_hash_table_lookup (display->window_ids, &xwindow) != NULL); g_hash_table_remove (display->window_ids, &xwindow); /* Remove any pending pings */ remove_pending_pings_for_window (display, xwindow); } MetaWorkspace* meta_display_get_workspace_by_index (MetaDisplay *display, int idx) { GList *tmp; /* should be robust, index is maybe from an app */ if (idx < 0) return NULL; tmp = g_list_nth (display->workspaces, idx); if (tmp == NULL) return NULL; else return tmp->data; } MetaWorkspace* meta_display_get_workspace_by_screen_index (MetaDisplay *display, MetaScreen *screen, int idx) { GList *tmp; int i; /* should be robust, idx is maybe from an app */ if (idx < 0) return NULL; i = 0; tmp = display->workspaces; while (tmp != NULL) { MetaWorkspace *w = tmp->data; if (w->screen == screen) { if (i == idx) return w; else ++i; } tmp = tmp->next; } return NULL; } Cursor meta_display_create_x_cursor (MetaDisplay *display, MetaCursor cursor) { Cursor xcursor; guint glyph; switch (cursor) { case META_CURSOR_DEFAULT: glyph = XC_left_ptr; break; case META_CURSOR_NORTH_RESIZE: glyph = XC_top_side; break; case META_CURSOR_SOUTH_RESIZE: glyph = XC_bottom_side; break; case META_CURSOR_WEST_RESIZE: glyph = XC_left_side; break; case META_CURSOR_EAST_RESIZE: glyph = XC_right_side; break; case META_CURSOR_SE_RESIZE: glyph = XC_bottom_right_corner; break; case META_CURSOR_SW_RESIZE: glyph = XC_bottom_left_corner; break; case META_CURSOR_NE_RESIZE: glyph = XC_top_right_corner; break; case META_CURSOR_NW_RESIZE: glyph = XC_top_left_corner; break; default: g_assert_not_reached (); glyph = 0; /* silence compiler */ break; } xcursor = XCreateFontCursor (display->xdisplay, glyph); return xcursor; } static Cursor xcursor_for_op (MetaDisplay *display, MetaGrabOp op) { MetaCursor cursor = META_CURSOR_DEFAULT; switch (op) { case META_GRAB_OP_RESIZING_SE: cursor = META_CURSOR_SE_RESIZE; break; case META_GRAB_OP_RESIZING_S: cursor = META_CURSOR_SOUTH_RESIZE; break; case META_GRAB_OP_RESIZING_SW: cursor = META_CURSOR_SW_RESIZE; break; case META_GRAB_OP_RESIZING_N: cursor = META_CURSOR_NORTH_RESIZE; break; case META_GRAB_OP_RESIZING_NE: cursor = META_CURSOR_NE_RESIZE; break; case META_GRAB_OP_RESIZING_NW: cursor = META_CURSOR_NW_RESIZE; break; case META_GRAB_OP_RESIZING_W: cursor = META_CURSOR_WEST_RESIZE; break; case META_GRAB_OP_RESIZING_E: cursor = META_CURSOR_EAST_RESIZE; break; default: break; } return meta_display_create_x_cursor (display, cursor); } gboolean meta_display_begin_grab_op (MetaDisplay *display, MetaWindow *window, MetaGrabOp op, gboolean pointer_already_grabbed, int button, gulong modmask, Time timestamp, int root_x, int root_y) { Window grab_xwindow; Cursor cursor; meta_topic (META_DEBUG_WINDOW_OPS, "Doing grab op %d on window %s button %d pointer already grabbed: %d\n", op, window->desc, button, pointer_already_grabbed); if (display->grab_op != META_GRAB_OP_NONE) { meta_warning ("Attempt to perform window operation %d on window %s when operation %d on %s already in effect\n", op, window->desc, display->grab_op, display->grab_window->desc); return FALSE; } grab_xwindow = window->frame ? window->frame->xwindow : window->xwindow; if (pointer_already_grabbed) display->grab_have_pointer = TRUE; cursor = xcursor_for_op (display, op); #define GRAB_MASK (PointerMotionMask | PointerMotionHintMask | \ ButtonPressMask | ButtonReleaseMask) meta_error_trap_push (display); if (XGrabPointer (display->xdisplay, grab_xwindow, False, GRAB_MASK, GrabModeAsync, GrabModeAsync, None, cursor, timestamp) == GrabSuccess) { display->grab_have_pointer = TRUE; meta_topic (META_DEBUG_WINDOW_OPS, "XGrabPointer() returned GrabSuccess\n"); } if (meta_error_trap_pop (display) != Success) { meta_topic (META_DEBUG_WINDOW_OPS, "Error trapped from XGrabPointer()\n"); if (display->grab_have_pointer) display->grab_have_pointer = FALSE; } #undef GRAB_MASK XFreeCursor (display->xdisplay, cursor); if (!display->grab_have_pointer) { meta_topic (META_DEBUG_WINDOW_OPS, "XGrabPointer() failed\n"); return FALSE; } if (grab_op_is_keyboard (op)) { if (meta_window_grab_all_keys (window)) display->grab_have_keyboard = TRUE; if (!display->grab_have_keyboard) { meta_topic (META_DEBUG_WINDOW_OPS, "grabbing all keys failed\n"); return FALSE; } } display->grab_op = op; display->grab_window = window; display->grab_xwindow = grab_xwindow; display->grab_button = button; display->grab_mask = modmask; display->grab_root_x = root_x; display->grab_root_y = root_y; display->grab_initial_window_pos = display->grab_window->rect; meta_window_get_position (display->grab_window, &display->grab_initial_window_pos.x, &display->grab_initial_window_pos.y); meta_topic (META_DEBUG_WINDOW_OPS, "Grab op %d on window %s successful\n", display->grab_op, display->grab_window->desc); g_assert (display->grab_window != NULL); g_assert (display->grab_op != META_GRAB_OP_NONE); /* Do this last, after everything is set up. */ switch (op) { case META_GRAB_OP_KEYBOARD_TABBING_NORMAL: meta_screen_ensure_tab_popup (window->screen, META_TAB_LIST_NORMAL); break; case META_GRAB_OP_KEYBOARD_TABBING_DOCK: meta_screen_ensure_tab_popup (window->screen, META_TAB_LIST_DOCKS); break; default: break; } meta_window_refresh_resize_popup (display->grab_window); return TRUE; } void meta_display_end_grab_op (MetaDisplay *display, Time timestamp) { if (display->grab_op == META_GRAB_OP_NONE) return; if (display->grab_op == META_GRAB_OP_KEYBOARD_TABBING_NORMAL || display->grab_op == META_GRAB_OP_KEYBOARD_TABBING_DOCK) { meta_ui_tab_popup_free (display->grab_window->screen->tab_popup); display->grab_window->screen->tab_popup = NULL; /* If the ungrab here causes an EnterNotify, ignore it for * sloppy focus */ display->ungrab_should_not_cause_focus_window = display->grab_xwindow; } if (display->grab_have_pointer) { meta_topic (META_DEBUG_WINDOW_OPS, "Ungrabbing pointer with timestamp %lu\n", timestamp); XUngrabPointer (display->xdisplay, timestamp); } if (display->grab_have_keyboard) { meta_topic (META_DEBUG_WINDOW_OPS, "Ungrabbing all keys\n"); meta_window_ungrab_all_keys (display->grab_window); } display->grab_window = NULL; display->grab_xwindow = None; display->grab_op = META_GRAB_OP_NONE; if (display->grab_resize_popup) { meta_ui_resize_popup_free (display->grab_resize_popup); display->grab_resize_popup = NULL; } } static void meta_change_button_grab (MetaDisplay *display, Window xwindow, gboolean grab, gboolean sync, int button, int modmask) { int ignored_mask; ignored_mask = 0; while (ignored_mask < (int) display->ignored_modifier_mask) { int result; if (ignored_mask & ~(display->ignored_modifier_mask)) { /* Not a combination of ignored modifiers * (it contains some non-ignored modifiers) */ ++ignored_mask; continue; } meta_error_trap_push (display); if (grab) XGrabButton (display->xdisplay, button, modmask | ignored_mask, xwindow, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | PointerMotionHintMask, sync ? GrabModeSync : GrabModeAsync, GrabModeAsync, False, None); else XUngrabButton (display->xdisplay, button, modmask | ignored_mask, xwindow); result = meta_error_trap_pop (display); if (result != Success) meta_verbose ("Failed to grab button %d with mask 0x%x for window 0x%lx error code %d\n", button, modmask | ignored_mask, xwindow, result); ++ignored_mask; } } void meta_display_grab_window_buttons (MetaDisplay *display, Window xwindow) { /* Grab Alt + button1 and Alt + button2 for moving window, * and Alt + button3 for popping up window menu. */ meta_verbose ("Grabbing window buttons for 0x%lx\n", xwindow); /* FIXME If we ignored errors here instead of spewing, we could * put one big error trap around the loop and avoid a bunch of * XSync() */ { gboolean debug = g_getenv ("METACITY_DEBUG_BUTTON_GRABS") != NULL; int i = 1; while (i < 4) { meta_change_button_grab (display, xwindow, TRUE, FALSE, i, Mod1Mask); /* This is for debugging, since I end up moving the Xnest * otherwise ;-) */ if (debug) meta_change_button_grab (display, xwindow, TRUE, FALSE, i, ControlMask); ++i; } } } void meta_display_ungrab_window_buttons (MetaDisplay *display, Window xwindow) { gboolean debug = g_getenv ("METACITY_DEBUG_BUTTON_GRABS") != NULL; int i = 1; while (i < 4) { meta_change_button_grab (display, xwindow, FALSE, FALSE, i, Mod1Mask); if (debug) meta_change_button_grab (display, xwindow, FALSE, FALSE, i, ControlMask); ++i; } } /* Grab buttons we only grab while unfocused in click-to-focus mode */ #define MAX_FOCUS_BUTTON 4 void meta_display_grab_focus_window_button (MetaDisplay *display, Window xwindow) { /* Grab button 1 for activating unfocused windows */ meta_verbose ("Grabbing unfocused window buttons for 0x%lx\n", xwindow); /* FIXME If we ignored errors here instead of spewing, we could * put one big error trap around the loop and avoid a bunch of * XSync() */ { int i = 1; while (i < MAX_FOCUS_BUTTON) { meta_change_button_grab (display, xwindow, TRUE, TRUE, i, 0); ++i; } } } void meta_display_ungrab_focus_window_button (MetaDisplay *display, Window xwindow) { meta_verbose ("Ungrabbing unfocused window buttons for 0x%lx\n", xwindow); { int i = 1; while (i < MAX_FOCUS_BUTTON) { meta_change_button_grab (display, xwindow, FALSE, TRUE, i, 0); ++i; } } } void meta_display_increment_event_serial (MetaDisplay *display) { /* We just make some random X request */ XDeleteProperty (display->xdisplay, display->leader_window, display->atom_motif_wm_hints); } void meta_display_update_active_window_hint (MetaDisplay *display) { GSList *tmp; unsigned long data[2]; if (display->focus_window) data[0] = display->focus_window->xwindow; else data[0] = None; data[1] = None; tmp = display->screens; while (tmp != NULL) { MetaScreen *screen = tmp->data; meta_error_trap_push (display); XChangeProperty (display->xdisplay, screen->xroot, display->atom_net_active_window, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 2); meta_error_trap_pop (display); tmp = tmp->next; } } static void queue_windows_showing (MetaDisplay *display) { GSList *windows; GSList *tmp; windows = meta_display_list_windows (display); tmp = windows; while (tmp != NULL) { meta_window_queue_calc_showing (tmp->data); tmp = tmp->next; } g_slist_free (windows); } void meta_display_show_desktop (MetaDisplay *display) { if (display->showing_desktop) return; display->showing_desktop = TRUE; queue_windows_showing (display); } void meta_display_unshow_desktop (MetaDisplay *display) { if (!display->showing_desktop) return; display->showing_desktop = FALSE; queue_windows_showing (display); } void meta_display_queue_retheme_all_windows (MetaDisplay *display) { GSList* windows; GSList *tmp; windows = meta_display_list_windows (display); tmp = windows; while (tmp != NULL) { MetaWindow *window = tmp->data; meta_window_queue_move_resize (window); if (window->frame) meta_frame_queue_draw (window->frame); tmp = tmp->next; } g_slist_free (windows); } void meta_display_retheme_all (void) { GSList *tmp; tmp = meta_displays_list (); while (tmp != NULL) { MetaDisplay *display = tmp->data; meta_display_queue_retheme_all_windows (display); tmp = tmp->next; } } static gboolean is_syncing = FALSE; gboolean meta_is_syncing (void) { return is_syncing; } void meta_set_syncing (gboolean setting) { if (setting != is_syncing) { GSList *tmp; is_syncing = setting; tmp = meta_displays_list (); while (tmp != NULL) { MetaDisplay *display = tmp->data; XSynchronize (display->xdisplay, is_syncing); tmp = tmp->next; } } } #define PING_TIMEOUT_DELAY 2250 static gboolean meta_display_ping_timeout (gpointer data) { MetaPingData *ping_data; ping_data = data; ping_data->ping_timeout_id = 0; meta_topic (META_DEBUG_PING, "Ping %lu on window %lx timed out\n", ping_data->timestamp, ping_data->xwindow); (* ping_data->ping_timeout_func) (ping_data->display, ping_data->xwindow, ping_data->user_data); ping_data->display->pending_pings = g_slist_remove (ping_data->display->pending_pings, ping_data); ping_data_free (ping_data); return FALSE; } void meta_display_ping_window (MetaDisplay *display, MetaWindow *window, Time timestamp, MetaWindowPingFunc ping_reply_func, MetaWindowPingFunc ping_timeout_func, gpointer user_data) { MetaPingData *ping_data; if (timestamp == CurrentTime) { meta_warning ("Tried to ping a window with CurrentTime! Not allowed.\n"); return; } if (!window->net_wm_ping) { if (ping_reply_func) (* ping_reply_func) (display, window->xwindow, user_data); return; } ping_data = g_new (MetaPingData, 1); ping_data->display = display; ping_data->xwindow = window->xwindow; ping_data->timestamp = timestamp; ping_data->ping_reply_func = ping_reply_func; ping_data->ping_timeout_func = ping_timeout_func; ping_data->user_data = user_data; ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, meta_display_ping_timeout, ping_data); display->pending_pings = g_slist_prepend (display->pending_pings, ping_data); meta_topic (META_DEBUG_PING, "Sending ping with timestamp %lu to window %s\n", timestamp, window->desc); meta_window_send_icccm_message (window, display->atom_net_wm_ping, timestamp); } /* process the pong from our ping */ static void process_pong_message (MetaDisplay *display, XEvent *event) { GSList *tmp; meta_topic (META_DEBUG_PING, "Received a pong with timestamp %lu\n", (Time) event->xclient.data.l[1]); for (tmp = display->pending_pings; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; if ((Time)event->xclient.data.l[1] == ping_data->timestamp) { meta_topic (META_DEBUG_PING, "Matching ping found for pong %lu\n", ping_data->timestamp); /* Remove the ping data from the list */ display->pending_pings = g_slist_remove (display->pending_pings, ping_data); /* Remove the timeout */ if (ping_data->ping_timeout_id != 0) { g_source_remove (ping_data->ping_timeout_id); ping_data->ping_timeout_id = 0; } /* Call callback */ (* ping_data->ping_reply_func) (display, ping_data->xwindow, ping_data->user_data); ping_data_free (ping_data); break; } } } gboolean meta_display_window_has_pending_pings (MetaDisplay *display, MetaWindow *window) { GSList *tmp; for (tmp = display->pending_pings; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; if (ping_data->xwindow == window->xwindow) return TRUE; } return FALSE; } #define IN_TAB_CHAIN(w,t) (((t) == META_TAB_LIST_NORMAL && META_WINDOW_IN_NORMAL_TAB_CHAIN (w)) || ((t) == META_TAB_LIST_DOCKS && META_WINDOW_IN_DOCK_TAB_CHAIN (w))) static MetaWindow* find_tab_forward (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, GList *start) { GList *tmp; g_return_val_if_fail (start != NULL, NULL); tmp = start->next; while (tmp != NULL) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_visible_on_workspace (window, workspace))) return window; tmp = tmp->next; } tmp = display->mru_list; while (tmp != start) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_visible_on_workspace (window, workspace))) return window; tmp = tmp->next; } return NULL; } static MetaWindow* find_tab_backward (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, GList *start) { GList *tmp; g_return_val_if_fail (start != NULL, NULL); tmp = start->prev; while (tmp != NULL) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_visible_on_workspace (window, workspace))) return window; tmp = tmp->prev; } tmp = g_list_last (display->mru_list); while (tmp != start) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_visible_on_workspace (window, workspace))) return window; tmp = tmp->prev; } return NULL; } GSList* meta_display_get_tab_list (MetaDisplay *display, MetaTabList type, MetaScreen *screen, MetaWorkspace *workspace) { GSList *tab_list; /* workspace can be NULL for all workspaces */ /* Windows sellout mode - MRU order */ { GList *tmp; tab_list = NULL; tmp = screen->display->mru_list; while (tmp != NULL) { MetaWindow *window = tmp->data; if (window->screen == screen && IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_visible_on_workspace (window, workspace))) tab_list = g_slist_prepend (tab_list, window); tmp = tmp->next; } tab_list = g_slist_reverse (tab_list); } return tab_list; } MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, MetaWindow *window, gboolean backward) { if (display->mru_list == NULL) return NULL; if (window != NULL) { g_assert (window->display == display); if (backward) return find_tab_backward (display, type, workspace, g_list_find (display->mru_list, window)); else return find_tab_forward (display, type, workspace, g_list_find (display->mru_list, window)); } if (backward) return find_tab_backward (display, type, workspace, g_list_last (display->mru_list)); else return find_tab_forward (display, type, workspace, display->mru_list); } int meta_resize_gravity_from_grab_op (MetaGrabOp op) { int gravity; gravity = -1; switch (op) { case META_GRAB_OP_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_SE: gravity = NorthWestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_S: case META_GRAB_OP_RESIZING_S: gravity = NorthGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_RESIZING_SW: gravity = NorthEastGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_N: case META_GRAB_OP_RESIZING_N: gravity = SouthGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_RESIZING_NE: gravity = SouthWestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_NW: case META_GRAB_OP_RESIZING_NW: gravity = SouthEastGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_E: case META_GRAB_OP_RESIZING_E: gravity = WestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_W: case META_GRAB_OP_RESIZING_W: gravity = EastGravity; break; default: break; } return gravity; }