mutter/clutter/x11/clutter-device-manager-core-x11.c
Emmanuele Bassi 75f81fee70 x11: Apply the window scaling factor
On high DPI density displays we create surfaces with a size scaled up by
a certain factor. Even if the contents stay at the same relative size
and position, we need to compensate the scaling both when changing the
surface size, and when dealing with input.

https://bugzilla.gnome.org/show_bug.cgi?id=705915
2013-09-19 22:51:52 +01:00

536 lines
18 KiB
C

/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright © 2009, 2010, 2011 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
#include "config.h"
#include "clutter-device-manager-core-x11.h"
#include "clutter-backend-x11.h"
#include "clutter-input-device-core-x11.h"
#include "clutter-stage-x11.h"
#include "clutter-backend.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event-private.h"
#include "clutter-event-translator.h"
#include "clutter-stage-private.h"
#include "clutter-private.h"
enum
{
PROP_0,
PROP_EVENT_BASE,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
#define clutter_device_manager_x11_get_type _clutter_device_manager_x11_get_type
G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerX11,
clutter_device_manager_x11,
CLUTTER_TYPE_DEVICE_MANAGER,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
clutter_event_translator_iface_init));
static inline void
translate_key_event (ClutterBackendX11 *backend_x11,
ClutterDeviceManagerX11 *manager_x11,
ClutterEvent *event,
XEvent *xevent)
{
ClutterEventX11 *event_x11;
char buffer[256 + 1];
int n;
event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS
: CLUTTER_KEY_RELEASE;
event->key.time = xevent->xkey.time;
clutter_event_set_device (event, manager_x11->core_keyboard);
/* KeyEvents have platform specific data associated to them */
event_x11 = _clutter_event_x11_new ();
_clutter_event_set_platform_data (event, event_x11);
event->key.modifier_state = (ClutterModifierType) xevent->xkey.state;
event->key.hardware_keycode = xevent->xkey.keycode;
/* keyval is the key ignoring all modifiers ('1' vs. '!') */
event->key.keyval =
_clutter_keymap_x11_translate_key_state (backend_x11->keymap,
event->key.hardware_keycode,
&event->key.modifier_state,
NULL);
event_x11->key_group =
_clutter_keymap_x11_get_key_group (backend_x11->keymap,
event->key.modifier_state);
event_x11->key_is_modifier =
_clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
event->key.hardware_keycode);
event_x11->num_lock_set =
_clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
event_x11->caps_lock_set =
_clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);
/* unicode_value is the printable representation */
n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL);
if (n != NoSymbol)
{
event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
if ((event->key.unicode_value != (gunichar) -1) &&
(event->key.unicode_value != (gunichar) -2))
goto out;
}
else
event->key.unicode_value = (gunichar)'\0';
out:
CLUTTER_NOTE (EVENT,
"%s: win:0x%x, key: %12s (%d)",
event->any.type == CLUTTER_KEY_PRESS
? "key press "
: "key release",
(unsigned int) xevent->xkey.window,
event->key.keyval ? buffer : "(none)",
event->key.keyval);
return;
}
static ClutterTranslateReturn
clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator,
gpointer native,
ClutterEvent *event)
{
ClutterDeviceManagerX11 *manager_x11;
ClutterBackendX11 *backend_x11;
ClutterStageX11 *stage_x11;
ClutterTranslateReturn res;
ClutterStage *stage;
XEvent *xevent;
int window_scale;
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (translator);
backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
xevent = native;
stage = clutter_x11_get_stage_from_window (xevent->xany.window);
if (stage == NULL)
return CLUTTER_TRANSLATE_CONTINUE;
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
return CLUTTER_TRANSLATE_CONTINUE;
stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
window_scale = stage_x11->scale_factor;
event->any.stage = stage;
res = CLUTTER_TRANSLATE_CONTINUE;
switch (xevent->type)
{
case KeyPress:
translate_key_event (backend_x11, manager_x11, event, xevent);
_clutter_stage_x11_set_user_time (stage_x11, xevent->xkey.time);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case KeyRelease:
/* old-style X11 terminals require that even modern X11 send
* KeyPress/KeyRelease pairs when auto-repeating. for this
* reason modern(-ish) API like XKB has a way to detect
* auto-repeat and do a single KeyRelease at the end of a
* KeyPress sequence.
*
* this check emulates XKB's detectable auto-repeat; we peek
* the next event and check if it's a KeyPress for the same key
* and timestamp - and then ignore it if it matches the
* KeyRelease
*
* if we have XKB, and autorepeat is enabled, then this becomes
* a no-op
*/
if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
{
XEvent next_event;
XPeekEvent (xevent->xkey.display, &next_event);
if (next_event.type == KeyPress &&
next_event.xkey.keycode == xevent->xkey.keycode &&
next_event.xkey.time == xevent->xkey.time)
{
res = CLUTTER_TRANSLATE_REMOVE;
break;
}
}
translate_key_event (backend_x11, manager_x11, event, xevent);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case ButtonPress:
CLUTTER_NOTE (EVENT,
"button press: win: 0x%x, coords: %d, %d, button: %d",
(unsigned int) stage_x11->xwin,
xevent->xbutton.x,
xevent->xbutton.y,
xevent->xbutton.button);
switch (xevent->xbutton.button)
{
case 4: /* up */
case 5: /* down */
case 6: /* left */
case 7: /* right */
event->scroll.type = CLUTTER_SCROLL;
if (xevent->xbutton.button == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xevent->xbutton.button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xevent->xbutton.button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.time = xevent->xbutton.time;
event->scroll.x = xevent->xbutton.x / window_scale;
event->scroll.y = xevent->xbutton.y / window_scale;
event->scroll.modifier_state = xevent->xbutton.state;
event->scroll.axes = NULL;
break;
default:
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x / window_scale;
event->button.y = xevent->xbutton.y / window_scale;
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
event->button.axes = NULL;
break;
}
clutter_event_set_device (event, manager_x11->core_pointer);
_clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case ButtonRelease:
CLUTTER_NOTE (EVENT,
"button press: win: 0x%x, coords: %d, %d, button: %d",
(unsigned int) stage_x11->xwin,
xevent->xbutton.x,
xevent->xbutton.y,
xevent->xbutton.button);
/* scroll events don't have a corresponding release */
if (xevent->xbutton.button == 4 ||
xevent->xbutton.button == 5 ||
xevent->xbutton.button == 6 ||
xevent->xbutton.button == 7)
{
res = CLUTTER_TRANSLATE_REMOVE;
break;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x / window_scale;
event->button.y = xevent->xbutton.y / window_scale;
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
event->button.axes = NULL;
clutter_event_set_device (event, manager_x11->core_pointer);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case MotionNotify:
CLUTTER_NOTE (EVENT,
"motion: win: 0x%x, coords: %d, %d",
(unsigned int) stage_x11->xwin,
xevent->xmotion.x,
xevent->xmotion.y);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xmotion.time;
event->motion.x = xevent->xmotion.x / window_scale;
event->motion.y = xevent->xmotion.y / window_scale;
event->motion.modifier_state = xevent->xmotion.state;
event->motion.axes = NULL;
clutter_event_set_device (event, manager_x11->core_pointer);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case EnterNotify:
CLUTTER_NOTE (EVENT, "Entering the stage (time:%u)",
(unsigned int) xevent->xcrossing.time);
event->crossing.type = CLUTTER_ENTER;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x / window_scale;
event->crossing.y = xevent->xcrossing.y / window_scale;
event->crossing.source = CLUTTER_ACTOR (stage);
event->crossing.related = NULL;
clutter_event_set_device (event, manager_x11->core_pointer);
_clutter_input_device_set_stage (manager_x11->core_pointer, stage);
res = CLUTTER_TRANSLATE_QUEUE;
break;
case LeaveNotify:
if (manager_x11->core_pointer->stage == NULL)
{
CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for "
"ButtonRelease event off-stage");
res = CLUTTER_TRANSLATE_REMOVE;
break;
}
/* we know that we are leaving the stage here */
CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
(unsigned int) xevent->xcrossing.time);
event->crossing.type = CLUTTER_LEAVE;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x / window_scale;
event->crossing.y = xevent->xcrossing.y / window_scale;
event->crossing.source = CLUTTER_ACTOR (stage);
event->crossing.related = NULL;
clutter_event_set_device (event, manager_x11->core_pointer);
_clutter_input_device_set_stage (manager_x11->core_pointer, NULL);
res = CLUTTER_TRANSLATE_QUEUE;
break;
default:
break;
}
return res;
}
static void
clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
{
iface->translate_event = clutter_device_manager_x11_translate_event;
}
static void
clutter_device_manager_x11_constructed (GObject *gobject)
{
ClutterDeviceManagerX11 *manager_x11;
ClutterBackendX11 *backend_x11;
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
g_object_get (gobject, "backend", &backend_x11, NULL);
g_assert (backend_x11 != NULL);
manager_x11->core_pointer =
g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
"name", "Core Pointer",
"has-cursor", TRUE,
"device-type", CLUTTER_POINTER_DEVICE,
"device-manager", manager_x11,
"device-mode", CLUTTER_INPUT_MODE_MASTER,
"backend", backend_x11,
"enabled", TRUE,
NULL);
CLUTTER_NOTE (BACKEND, "Added core pointer device");
manager_x11->core_keyboard =
g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
"name", "Core Keyboard",
"has-cursor", FALSE,
"device-type", CLUTTER_KEYBOARD_DEVICE,
"device-manager", manager_x11,
"device-mode", CLUTTER_INPUT_MODE_MASTER,
"backend", backend_x11,
"enabled", TRUE,
NULL);
CLUTTER_NOTE (BACKEND, "Added core keyboard device");
/* associate core devices */
_clutter_input_device_set_associated_device (manager_x11->core_pointer,
manager_x11->core_keyboard);
_clutter_input_device_set_associated_device (manager_x11->core_keyboard,
manager_x11->core_pointer);
if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed)
G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject);
}
static void
clutter_device_manager_x11_add_device (ClutterDeviceManager *manager,
ClutterInputDevice *device)
{
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
manager_x11->devices = g_slist_prepend (manager_x11->devices, device);
g_hash_table_replace (manager_x11->devices_by_id,
GINT_TO_POINTER (device->id),
device);
/* blow the cache */
g_slist_free (manager_x11->all_devices);
manager_x11->all_devices = NULL;
}
static void
clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager,
ClutterInputDevice *device)
{
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
g_hash_table_remove (manager_x11->devices_by_id,
GINT_TO_POINTER (device->id));
manager_x11->devices = g_slist_remove (manager_x11->devices, device);
/* blow the cache */
g_slist_free (manager_x11->all_devices);
manager_x11->all_devices = NULL;
}
static const GSList *
clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager)
{
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
/* cache the devices list so that we can keep the core pointer
* and keyboard outside of the ManagerX11:devices list
*/
if (manager_x11->all_devices == NULL)
{
GSList *all_devices = manager_x11->devices;
all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard);
all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer);
manager_x11->all_devices = all_devices;
}
return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices;
}
static ClutterInputDevice *
clutter_device_manager_x11_get_core_device (ClutterDeviceManager *manager,
ClutterInputDeviceType type)
{
ClutterDeviceManagerX11 *manager_x11;
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
switch (type)
{
case CLUTTER_POINTER_DEVICE:
return manager_x11->core_pointer;
case CLUTTER_KEYBOARD_DEVICE:
return manager_x11->core_keyboard;
default:
return NULL;
}
return NULL;
}
static ClutterInputDevice *
clutter_device_manager_x11_get_device (ClutterDeviceManager *manager,
gint id)
{
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
return g_hash_table_lookup (manager_x11->devices_by_id,
GINT_TO_POINTER (id));
}
static void
clutter_device_manager_x11_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
switch (prop_id)
{
case PROP_EVENT_BASE:
manager_x11->xi_event_base = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass)
{
ClutterDeviceManagerClass *manager_class;
GObjectClass *gobject_class;
obj_props[PROP_EVENT_BASE] =
g_param_spec_int ("event-base",
"Event Base",
"The first XI event",
-1, G_MAXINT,
-1,
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = clutter_device_manager_x11_constructed;
gobject_class->set_property = clutter_device_manager_x11_set_property;
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
manager_class->add_device = clutter_device_manager_x11_add_device;
manager_class->remove_device = clutter_device_manager_x11_remove_device;
manager_class->get_devices = clutter_device_manager_x11_get_devices;
manager_class->get_core_device = clutter_device_manager_x11_get_core_device;
manager_class->get_device = clutter_device_manager_x11_get_device;
}
static void
clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self)
{
self->devices_by_id = g_hash_table_new (NULL, NULL);
}