/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-dnd-private.h" #include "backends/x11/meta-barrier-x11.h" #include "backends/x11/meta-cursor-renderer-x11.h" #include "backends/x11/meta-cursor-tracker-x11.h" #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-input-settings-x11.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include "backends/x11/cm/meta-renderer-x11-cm.h" #include "compositor/meta-compositor-x11.h" #include "core/display-private.h" enum { PROP_0, PROP_DISPLAY_NAME, N_PROPS }; static GParamSpec *obj_props[N_PROPS]; struct _MetaBackendX11Cm { MetaBackendX11 parent; char *display_name; MetaCursorRenderer *cursor_renderer; char *keymap_layouts; char *keymap_variants; char *keymap_options; int locked_group; MetaInputSettings *input_settings; }; G_DEFINE_TYPE (MetaBackendX11Cm, meta_backend_x11_cm, META_TYPE_BACKEND_X11) static void apply_keymap (MetaBackendX11 *x11); static void take_touch_grab (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { META_VIRTUAL_CORE_POINTER_ID, sizeof (mask_bits), mask_bits }; XIGrabModifiers mods = { XIAnyModifier, 0 }; XISetMask (mask.mask, XI_TouchBegin); XISetMask (mask.mask, XI_TouchUpdate); XISetMask (mask.mask, XI_TouchEnd); XIGrabTouchBegin (xdisplay, META_VIRTUAL_CORE_POINTER_ID, DefaultRootWindow (xdisplay), False, &mask, 1, &mods); } static void on_device_added (ClutterSeat *seat, ClutterInputDevice *device, gpointer user_data) { MetaBackendX11 *x11 = META_BACKEND_X11 (user_data); if (clutter_input_device_get_device_type (device) == CLUTTER_KEYBOARD_DEVICE) apply_keymap (x11); } static void meta_backend_x11_cm_post_init (MetaBackend *backend) { MetaBackendClass *parent_backend_class = META_BACKEND_CLASS (meta_backend_x11_cm_parent_class); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (backend); ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_connect_object (seat, "device-added", G_CALLBACK (on_device_added), backend, 0); x11_cm->input_settings = g_object_new (META_TYPE_INPUT_SETTINGS_X11, "backend", backend, NULL); parent_backend_class->post_init (backend); take_touch_grab (backend); } static MetaBackendCapabilities meta_backend_x11_cm_get_capabilities (MetaBackend *backend) { MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); MetaBackendCapabilities capabilities = META_BACKEND_CAPABILITY_NONE; MetaX11Barriers *barriers; barriers = meta_backend_x11_get_barriers (backend_x11); if (barriers) capabilities |= META_BACKEND_CAPABILITY_BARRIERS; return capabilities; } static MetaRenderer * meta_backend_x11_cm_create_renderer (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_RENDERER_X11_CM, "backend", backend, NULL); } static MetaMonitorManager * meta_backend_x11_cm_create_monitor_manager (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_MONITOR_MANAGER_XRANDR, "backend", backend, NULL); } static MetaCursorRenderer * meta_backend_x11_cm_get_cursor_renderer (MetaBackend *backend, ClutterInputDevice *device) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (backend); if (!x11_cm->cursor_renderer) { x11_cm->cursor_renderer = g_object_new (META_TYPE_CURSOR_RENDERER_X11, "backend", backend, "device", device, NULL); } return x11_cm->cursor_renderer; } static MetaCursorTracker * meta_backend_x11_cm_create_cursor_tracker (MetaBackend *backend) { return g_object_new (META_TYPE_CURSOR_TRACKER_X11, "backend", backend, NULL); } static MetaInputSettings * meta_backend_x11_cm_get_input_settings (MetaBackend *backend) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (backend); return x11_cm->input_settings; } static void meta_backend_x11_cm_update_screen_size (MetaBackend *backend, int width, int height) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); Window xwin = meta_backend_x11_get_xwindow (x11); XResizeWindow (xdisplay, xwin, width, height); } static void meta_backend_x11_cm_select_stage_events (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); Window xwin = meta_backend_x11_get_xwindow (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); XISetMask (mask.mask, XI_Motion); XISelectEvents (xdisplay, xwin, &mask, 1); } static void get_xkbrf_var_defs (Display *xdisplay, const char *layouts, const char *variants, const char *options, char **rules_p, XkbRF_VarDefsRec *var_defs) { char *rules = NULL; /* Get it from the X property or fallback on defaults */ if (!XkbRF_GetNamesProp (xdisplay, &rules, var_defs) || !rules) { rules = strdup (DEFAULT_XKB_RULES_FILE); var_defs->model = strdup (DEFAULT_XKB_MODEL); var_defs->layout = NULL; var_defs->variant = NULL; var_defs->options = NULL; } /* Swap in our new options... */ free (var_defs->layout); var_defs->layout = strdup (layouts); free (var_defs->variant); var_defs->variant = strdup (variants); free (var_defs->options); var_defs->options = strdup (options); /* Sometimes, the property is a file path, and sometimes it's not. Normalize it so it's always a file path. */ if (rules[0] == '/') *rules_p = g_strdup (rules); else *rules_p = g_build_filename (XKB_BASE, "rules", rules, NULL); free (rules); } static void free_xkbrf_var_defs (XkbRF_VarDefsRec *var_defs) { free (var_defs->model); free (var_defs->layout); free (var_defs->variant); free (var_defs->options); } static void free_xkb_component_names (XkbComponentNamesRec *p) { free (p->keymap); free (p->keycodes); free (p->types); free (p->compat); free (p->symbols); free (p->geometry); } static void upload_xkb_description (Display *xdisplay, const gchar *rules_file_path, XkbRF_VarDefsRec *var_defs, XkbComponentNamesRec *comp_names) { XkbDescRec *xkb_desc; gchar *rules_file; /* Upload it to the X server using the same method as setxkbmap */ xkb_desc = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, comp_names, XkbGBN_AllComponentsMask, XkbGBN_AllComponentsMask & (~XkbGBN_GeometryMask), True); if (!xkb_desc) { g_warning ("Couldn't upload new XKB keyboard description"); return; } XkbFreeKeyboard (xkb_desc, 0, True); rules_file = g_path_get_basename (rules_file_path); if (!XkbRF_SetNamesProp (xdisplay, rules_file, var_defs)) g_warning ("Couldn't update the XKB root window property"); g_free (rules_file); } static void apply_keymap (MetaBackendX11 *x11) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); XkbRF_RulesRec *xkb_rules; XkbRF_VarDefsRec xkb_var_defs = { 0 }; char *rules_file_path; if (!x11_cm->keymap_layouts || !x11_cm->keymap_variants || !x11_cm->keymap_options) return; get_xkbrf_var_defs (xdisplay, x11_cm->keymap_layouts, x11_cm->keymap_variants, x11_cm->keymap_options, &rules_file_path, &xkb_var_defs); xkb_rules = XkbRF_Load (rules_file_path, NULL, True, True); if (xkb_rules) { XkbComponentNamesRec xkb_comp_names = { 0 }; XkbRF_GetComponents (xkb_rules, &xkb_var_defs, &xkb_comp_names); upload_xkb_description (xdisplay, rules_file_path, &xkb_var_defs, &xkb_comp_names); free_xkb_component_names (&xkb_comp_names); XkbRF_Free (xkb_rules, True); } else { g_warning ("Couldn't load XKB rules"); } free_xkbrf_var_defs (&xkb_var_defs); g_free (rules_file_path); } static void meta_backend_x11_cm_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); g_free (x11_cm->keymap_layouts); x11_cm->keymap_layouts = g_strdup (layouts); g_free (x11_cm->keymap_variants); x11_cm->keymap_variants = g_strdup (variants); g_free (x11_cm->keymap_options); x11_cm->keymap_options = g_strdup (options); apply_keymap (x11); } static void meta_backend_x11_cm_lock_layout_group (MetaBackend *backend, guint idx) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); x11_cm->locked_group = idx; XkbLockGroup (xdisplay, XkbUseCoreKbd, idx); } static gboolean meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *x11, XEvent *event) { MetaBackend *backend = META_BACKEND (x11); MetaContext *context = meta_backend_get_context (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); MetaDisplay *display; display = meta_context_get_display (context); if (display) { MetaCompositor *compositor = display->compositor; MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); if (meta_dnd_handle_xdnd_event (backend, compositor_x11, xdisplay, event)) return TRUE; } if (event->type == meta_backend_x11_get_xkb_event_base (x11)) { XkbEvent *xkb_ev = (XkbEvent *) event; if (xkb_ev->any.device == META_VIRTUAL_CORE_KEYBOARD_ID) { switch (xkb_ev->any.xkb_type) { case XkbStateNotify: if (xkb_ev->state.changed & XkbGroupLockMask) { if (x11_cm->locked_group != xkb_ev->state.locked_group) XkbLockGroup (xdisplay, XkbUseCoreKbd, x11_cm->locked_group); } break; default: break; } } } if (meta_monitor_manager_xrandr_handle_xevent (monitor_manager_xrandr, event)) return TRUE; return FALSE; } static void meta_backend_x11_cm_translate_device_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { Window stage_window = meta_backend_x11_get_xwindow (x11); if (device_event->event != stage_window) { device_event->event = stage_window; /* As an X11 compositor, the stage window is always at 0,0, so * using root coordinates will give us correct stage coordinates * as well... */ device_event->event_x = device_event->root_x; device_event->event_y = device_event->root_y; } } static void meta_backend_x11_cm_translate_crossing_event (MetaBackendX11 *x11, XIEnterEvent *enter_event) { Window stage_window = meta_backend_x11_get_xwindow (x11); if (enter_event->event != stage_window) { enter_event->event = stage_window; enter_event->event_x = enter_event->root_x; enter_event->event_y = enter_event->root_y; } } static void meta_backend_x11_cm_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaBackendX11Cm *backend_x11_cm = META_BACKEND_X11_CM (object); switch (prop_id) { case PROP_DISPLAY_NAME: backend_x11_cm->display_name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_backend_x11_cm_finalize (GObject *object) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (object); g_clear_pointer (&x11_cm->display_name, g_free); G_OBJECT_CLASS (meta_backend_x11_cm_parent_class)->finalize (object); } static void meta_backend_x11_cm_constructed (GObject *object) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (object); const char *display_name; if (x11_cm->display_name) display_name = (const char *) x11_cm->display_name; else display_name = g_getenv ("MUTTER_DISPLAY"); if (display_name) g_setenv ("DISPLAY", display_name, TRUE); G_OBJECT_CLASS (meta_backend_x11_cm_parent_class)->constructed (object); } static void meta_backend_x11_cm_init (MetaBackendX11Cm *backend_x11_cm) { MetaGpuXrandr *gpu_xrandr; /* * The X server deals with multiple GPUs for us, so we just see what the X * server gives us as one single GPU, even though it may actually be backed * by multiple. */ gpu_xrandr = meta_gpu_xrandr_new (META_BACKEND_X11 (backend_x11_cm)); meta_backend_add_gpu (META_BACKEND (backend_x11_cm), META_GPU (gpu_xrandr)); } static void meta_backend_x11_cm_class_init (MetaBackendX11CmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_CLASS (klass); object_class->set_property = meta_backend_x11_cm_set_property; object_class->finalize = meta_backend_x11_cm_finalize; object_class->constructed = meta_backend_x11_cm_constructed; backend_class->post_init = meta_backend_x11_cm_post_init; backend_class->get_capabilities = meta_backend_x11_cm_get_capabilities; backend_class->create_renderer = meta_backend_x11_cm_create_renderer; backend_class->create_monitor_manager = meta_backend_x11_cm_create_monitor_manager; backend_class->get_cursor_renderer = meta_backend_x11_cm_get_cursor_renderer; backend_class->create_cursor_tracker = meta_backend_x11_cm_create_cursor_tracker; backend_class->get_input_settings = meta_backend_x11_cm_get_input_settings; backend_class->update_screen_size = meta_backend_x11_cm_update_screen_size; backend_class->select_stage_events = meta_backend_x11_cm_select_stage_events; backend_class->lock_layout_group = meta_backend_x11_cm_lock_layout_group; backend_class->set_keymap = meta_backend_x11_cm_set_keymap; backend_x11_class->handle_host_xevent = meta_backend_x11_cm_handle_host_xevent; backend_x11_class->translate_device_event = meta_backend_x11_cm_translate_device_event; backend_x11_class->translate_crossing_event = meta_backend_x11_cm_translate_crossing_event; obj_props[PROP_DISPLAY_NAME] = g_param_spec_string ("display-name", "display name", "X11 display name", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); }