1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
4
* Copyright (C) 2010,2011 Red Hat, Inc.
6
* Author: Bastien Nocera <hadess@hadess.net>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31
#include <gudev/gudev.h>
33
#define GNOME_DESKTOP_USE_UNSTABLE_API
34
#include <libgnome-desktop/gnome-rr.h>
36
#include "gsd-input-helper.h"
37
#include "gnome-settings-plugin.h"
38
#include "gnome-settings-profile.h"
39
#include "gsd-orientation-manager.h"
42
ORIENTATION_UNDEFINED,
44
ORIENTATION_BOTTOM_UP,
49
#define GSD_ORIENTATION_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_ORIENTATION_MANAGER, GsdOrientationManagerPrivate))
51
struct GsdOrientationManagerPrivate
58
OrientationUp prev_orientation;
61
GDBusNodeInfo *introspection_data;
62
GDBusConnection *connection;
63
GDBusProxy *xrandr_proxy;
64
GCancellable *cancellable;
69
gboolean orientation_lock;
72
#define CONF_SCHEMA "org.gnome.settings-daemon.peripherals.touchscreen"
73
#define ORIENTATION_LOCK_KEY "orientation-lock"
75
#define GSD_ORIENTATION_DBUS_NAME GSD_DBUS_NAME ".Orientation"
76
#define GSD_ORIENTATION_DBUS_PATH GSD_DBUS_PATH "/Orientation"
78
static const gchar introspection_xml[] =
80
" <interface name='org.gnome.SettingsDaemon.Orientation'>"
81
" <annotation name='org.freedesktop.DBus.GLib.CSymbol' value='gsd_orientation_manager'/>"
85
static void gsd_orientation_manager_class_init (GsdOrientationManagerClass *klass);
86
static void gsd_orientation_manager_init (GsdOrientationManager *orientation_manager);
87
static void gsd_orientation_manager_finalize (GObject *object);
89
G_DEFINE_TYPE (GsdOrientationManager, gsd_orientation_manager, G_TYPE_OBJECT)
91
static gpointer manager_object = NULL;
93
#define MPU_THRESHOLD 12000
94
#define MPU_POLL_INTERVAL 1
96
static gboolean is_mpu6050 = FALSE;
97
static char *mpu6050_accel_x = NULL;
98
static char *mpu6050_accel_y = NULL;
99
static gboolean mpu_timer(GsdOrientationManager *manager);
102
gsd_orientation_manager_constructor (GType type,
103
guint n_construct_properties,
104
GObjectConstructParam *construct_properties)
106
GsdOrientationManager *orientation_manager;
108
orientation_manager = GSD_ORIENTATION_MANAGER (G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->constructor (type,
109
n_construct_properties,
110
construct_properties));
112
return G_OBJECT (orientation_manager);
116
gsd_orientation_manager_dispose (GObject *object)
118
G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->dispose (object);
122
gsd_orientation_manager_class_init (GsdOrientationManagerClass *klass)
124
GObjectClass *object_class = G_OBJECT_CLASS (klass);
126
object_class->constructor = gsd_orientation_manager_constructor;
127
object_class->dispose = gsd_orientation_manager_dispose;
128
object_class->finalize = gsd_orientation_manager_finalize;
130
g_type_class_add_private (klass, sizeof (GsdOrientationManagerPrivate));
134
gsd_orientation_manager_init (GsdOrientationManager *manager)
136
manager->priv = GSD_ORIENTATION_MANAGER_GET_PRIVATE (manager);
137
manager->priv->prev_orientation = ORIENTATION_UNDEFINED;
140
static GnomeRRRotation
141
orientation_to_rotation (OrientationUp orientation)
143
switch (orientation) {
144
case ORIENTATION_NORMAL:
145
return GNOME_RR_ROTATION_0;
146
case ORIENTATION_BOTTOM_UP:
147
return GNOME_RR_ROTATION_180;
148
case ORIENTATION_LEFT_UP:
149
return GNOME_RR_ROTATION_90;
150
case ORIENTATION_RIGHT_UP:
151
return GNOME_RR_ROTATION_270;
153
g_assert_not_reached ();
158
orientation_from_string (const char *orientation)
160
if (g_strcmp0 (orientation, "normal") == 0)
161
return ORIENTATION_NORMAL;
162
if (g_strcmp0 (orientation, "bottom-up") == 0)
163
return ORIENTATION_BOTTOM_UP;
164
if (g_strcmp0 (orientation, "left-up") == 0)
165
return ORIENTATION_LEFT_UP;
166
if (g_strcmp0 (orientation, "right-up") == 0)
167
return ORIENTATION_RIGHT_UP;
169
return ORIENTATION_UNDEFINED;
173
orientation_to_string (OrientationUp o)
176
case ORIENTATION_UNDEFINED:
178
case ORIENTATION_NORMAL:
180
case ORIENTATION_BOTTOM_UP:
182
case ORIENTATION_LEFT_UP:
184
case ORIENTATION_RIGHT_UP:
187
g_assert_not_reached ();
192
get_orientation_from_device (GUdevDevice *dev)
196
value = g_udev_device_get_property (dev, "ID_INPUT_ACCELEROMETER_ORIENTATION");
198
g_debug ("Couldn't find orientation for accelerometer %s",
199
g_udev_device_get_sysfs_path (dev));
200
return ORIENTATION_UNDEFINED;
202
g_debug ("Found orientation '%s' for accelerometer %s",
203
value, g_udev_device_get_sysfs_path (dev));
205
return orientation_from_string (value);
209
on_xrandr_action_call_finished (GObject *source_object,
211
GsdOrientationManager *manager)
213
GError *error = NULL;
216
variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
218
g_object_unref (manager->priv->cancellable);
219
manager->priv->cancellable = NULL;
222
g_warning ("Unable to call 'RotateTo': %s", error->message);
223
g_error_free (error);
225
g_variant_unref (variant);
230
do_xrandr_action (GsdOrientationManager *manager,
231
GnomeRRRotation rotation)
233
GsdOrientationManagerPrivate *priv = manager->priv;
237
if (priv->connection == NULL || priv->xrandr_proxy == NULL) {
238
g_warning ("No existing D-Bus connection trying to handle XRANDR keys");
242
if (priv->cancellable != NULL) {
243
g_debug ("xrandr action already in flight");
247
g_get_current_time (&tv);
248
timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
250
priv->cancellable = g_cancellable_new ();
252
g_dbus_proxy_call (priv->xrandr_proxy,
254
g_variant_new ("(ix)", rotation, timestamp),
255
G_DBUS_CALL_FLAGS_NONE,
258
(GAsyncReadyCallback) on_xrandr_action_call_finished,
263
do_rotation (GsdOrientationManager *manager)
265
GnomeRRRotation rotation;
267
if (manager->priv->orientation_lock) {
268
g_debug ("Orientation changed, but we are locked");
271
if (manager->priv->prev_orientation == ORIENTATION_UNDEFINED) {
272
g_debug ("Not trying to rotate, orientation is undefined");
276
rotation = orientation_to_rotation (manager->priv->prev_orientation);
278
do_xrandr_action (manager, rotation);
282
client_uevent_cb (GUdevClient *client,
285
GsdOrientationManager *manager)
287
const char *sysfs_path;
288
OrientationUp orientation;
290
sysfs_path = g_udev_device_get_sysfs_path (device);
291
g_debug ("Received uevent '%s' from '%s'", action, sysfs_path);
293
if (manager->priv->orientation_lock)
296
if (g_str_equal (action, "change") == FALSE)
299
if (g_strcmp0 (manager->priv->sysfs_path, sysfs_path) != 0)
302
g_debug ("Received an event from the accelerometer");
304
orientation = get_orientation_from_device (device);
305
if (orientation != manager->priv->prev_orientation) {
306
manager->priv->prev_orientation = orientation;
307
g_debug ("Orientation changed to '%s', switching screen rotation",
308
orientation_to_string (manager->priv->prev_orientation));
310
do_rotation (manager);
315
orientation_lock_changed_cb (GSettings *settings,
317
GsdOrientationManager *manager)
321
new = g_settings_get_boolean (settings, key);
322
if (new == manager->priv->orientation_lock)
325
manager->priv->orientation_lock = new;
329
g_timeout_add_seconds(MPU_POLL_INTERVAL, (GSourceFunc) mpu_timer, manager);
331
/* Handle the rotations that could have occurred while
333
do_rotation (manager);
338
xrandr_ready_cb (GObject *source_object,
340
GsdOrientationManager *manager)
342
GError *error = NULL;
344
manager->priv->xrandr_proxy = g_dbus_proxy_new_finish (res, &error);
345
if (manager->priv->xrandr_proxy == NULL) {
346
g_warning ("Failed to get proxy for XRandR operations: %s", error->message);
347
g_error_free (error);
352
on_bus_gotten (GObject *source_object,
354
GsdOrientationManager *manager)
356
GDBusConnection *connection;
357
GError *error = NULL;
359
connection = g_bus_get_finish (res, &error);
360
if (connection == NULL) {
361
g_warning ("Could not get session bus: %s", error->message);
362
g_error_free (error);
365
manager->priv->connection = connection;
367
g_dbus_connection_register_object (connection,
368
GSD_ORIENTATION_DBUS_PATH,
369
manager->priv->introspection_data->interfaces[0],
375
g_dbus_proxy_new (manager->priv->connection,
376
G_DBUS_PROXY_FLAGS_NONE,
378
GSD_DBUS_NAME ".XRANDR",
379
GSD_DBUS_PATH "/XRANDR",
380
GSD_DBUS_BASE_INTERFACE ".XRANDR_2",
382
(GAsyncReadyCallback) xrandr_ready_cb,
385
manager->priv->name_id = g_bus_own_name_on_connection (connection,
386
GSD_ORIENTATION_DBUS_NAME,
387
G_BUS_NAME_OWNER_FLAGS_NONE,
395
get_accelerometer (GUdevClient *client)
397
GList *list, *listiio, *l;
398
GUdevDevice *ret, *parent;
400
/* Look for a device with the ID_INPUT_ACCELEROMETER=1 property */
402
list = g_udev_client_query_by_subsystem (client, "input");
403
listiio = g_udev_client_query_by_subsystem (client, "iio");
404
list = g_list_concat(list, listiio);
405
for (l = list; l != NULL; l = l->next) {
409
if (g_udev_device_get_property_as_boolean (dev, "ID_INPUT_ACCELEROMETER")) {
413
g_object_unref (dev);
420
/* Now walk up to the parent */
421
parent = g_udev_device_get_parent (ret);
425
if (g_udev_device_get_property_as_boolean (parent, "ID_INPUT_ACCELEROMETER")) {
426
g_object_unref (ret);
429
g_object_unref (parent);
435
static int read_sysfs_attr_as_int(const char *filename) {
438
int fd = open(filename, O_RDONLY);
441
c = read(fd, buf, 40);
445
sscanf(buf, "%d", &i);
450
static gboolean mpu_timer(GsdOrientationManager *manager) {
452
static gboolean first = TRUE;
453
OrientationUp orientation = manager->priv->prev_orientation;
455
if (manager->priv->xrandr_proxy == NULL)
458
x = read_sysfs_attr_as_int(mpu6050_accel_x);
459
y = read_sysfs_attr_as_int(mpu6050_accel_y);
461
if (x > MPU_THRESHOLD)
462
orientation = ORIENTATION_NORMAL;
463
if (x < -MPU_THRESHOLD)
464
orientation = ORIENTATION_BOTTOM_UP;
465
if (y > MPU_THRESHOLD)
466
orientation = ORIENTATION_RIGHT_UP;
467
if (y < -MPU_THRESHOLD)
468
orientation = ORIENTATION_LEFT_UP;
470
if (orientation != manager->priv->prev_orientation || first) {
472
manager->priv->prev_orientation = orientation;
473
g_debug ("Orientation changed to '%s', switching screen rotation",
474
orientation_to_string (manager->priv->prev_orientation));
476
do_rotation (manager);
479
return !manager->priv->orientation_lock;
483
gsd_orientation_manager_idle_cb (GsdOrientationManager *manager)
485
const char * const subsystems[] = { "input", NULL };
488
gnome_settings_profile_start (NULL);
490
manager->priv->settings = g_settings_new (CONF_SCHEMA);
491
manager->priv->orientation_lock = g_settings_get_boolean (manager->priv->settings, ORIENTATION_LOCK_KEY);
492
g_signal_connect (G_OBJECT (manager->priv->settings), "changed::orientation-lock",
493
G_CALLBACK (orientation_lock_changed_cb), manager);
495
manager->priv->client = g_udev_client_new (subsystems);
496
dev = get_accelerometer (manager->priv->client);
498
g_debug ("Did not find an accelerometer");
499
gnome_settings_profile_end (NULL);
502
manager->priv->sysfs_path = g_strdup (g_udev_device_get_sysfs_path (dev));
503
g_debug ("Found accelerometer at sysfs path '%s'", manager->priv->sysfs_path);
505
manager->priv->prev_orientation = get_orientation_from_device (dev);
507
/* Poll the sysfs attributes exposed by MPU6050 as it is not an uevent based input driver */
508
if (g_strcmp0 (g_udev_device_get_sysfs_attr (dev, "name"), "mpu6050") == 0) {
509
manager->priv->prev_orientation = ORIENTATION_NORMAL;
510
g_timeout_add_seconds(MPU_POLL_INTERVAL, (GSourceFunc) mpu_timer, manager);
511
mpu6050_accel_x = g_build_filename(manager->priv->sysfs_path, "in_accel_x_raw", NULL);
512
mpu6050_accel_y = g_build_filename(manager->priv->sysfs_path, "in_accel_y_raw", NULL);
516
g_object_unref (dev);
518
/* Start process of owning a D-Bus name */
519
g_bus_get (G_BUS_TYPE_SESSION,
521
(GAsyncReadyCallback) on_bus_gotten,
524
g_signal_connect (G_OBJECT (manager->priv->client), "uevent",
525
G_CALLBACK (client_uevent_cb), manager);
527
gnome_settings_profile_end (NULL);
533
gsd_orientation_manager_start (GsdOrientationManager *manager,
536
gnome_settings_profile_start (NULL);
538
manager->priv->start_idle_id = g_idle_add ((GSourceFunc) gsd_orientation_manager_idle_cb, manager);
540
manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
541
g_assert (manager->priv->introspection_data != NULL);
543
gnome_settings_profile_end (NULL);
549
gsd_orientation_manager_stop (GsdOrientationManager *manager)
551
GsdOrientationManagerPrivate *p = manager->priv;
553
g_debug ("Stopping orientation manager");
556
g_object_unref (p->settings);
561
g_free (p->sysfs_path);
562
p->sysfs_path = NULL;
565
if (p->introspection_data) {
566
g_dbus_node_info_unref (p->introspection_data);
567
p->introspection_data = NULL;
571
g_object_unref (p->client);
577
gsd_orientation_manager_finalize (GObject *object)
579
GsdOrientationManager *orientation_manager;
581
g_return_if_fail (object != NULL);
582
g_return_if_fail (GSD_IS_ORIENTATION_MANAGER (object));
584
orientation_manager = GSD_ORIENTATION_MANAGER (object);
586
g_return_if_fail (orientation_manager->priv != NULL);
588
if (orientation_manager->priv->start_idle_id != 0)
589
g_source_remove (orientation_manager->priv->start_idle_id);
591
if (orientation_manager->priv->name_id != 0)
592
g_bus_unown_name (orientation_manager->priv->name_id);
594
G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->finalize (object);
597
GsdOrientationManager *
598
gsd_orientation_manager_new (void)
600
if (manager_object != NULL) {
601
g_object_ref (manager_object);
603
manager_object = g_object_new (GSD_TYPE_ORIENTATION_MANAGER, NULL);
604
g_object_add_weak_pointer (manager_object,
605
(gpointer *) &manager_object);
608
return GSD_ORIENTATION_MANAGER (manager_object);