1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27
#include <glib/gi18n.h>
28
#include <glib-object.h>
32
#include <systemd/sd-daemon.h>
35
#include "gdm-manager.h"
36
#include "gdm-display-factory.h"
37
#include "gdm-local-display-factory.h"
38
#include "gdm-local-display-factory-glue.h"
40
#include "gdm-display-store.h"
41
#include "gdm-static-display.h"
42
#include "gdm-transient-display.h"
44
#define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate))
46
#define CK_SEAT1_PATH "/org/freedesktop/ConsoleKit/Seat1"
47
#define SYSTEMD_SEAT0_PATH "seat0"
49
#define GDM_DBUS_PATH "/org/gnome/DisplayManager"
50
#define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory"
51
#define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory"
53
#define MAX_DISPLAY_FAILURES 5
55
struct GdmLocalDisplayFactoryPrivate
57
GdmDBusLocalDisplayFactory *skeleton;
58
GDBusConnection *connection;
61
/* FIXME: this needs to be per seat? */
66
guint seat_removed_id;
74
static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass);
75
static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory);
76
static void gdm_local_display_factory_finalize (GObject *object);
78
static GdmDisplay *create_display (GdmLocalDisplayFactory *factory,
80
gboolean initial_display);
82
static void on_display_status_changed (GdmDisplay *display,
84
GdmLocalDisplayFactory *factory);
86
static gpointer local_display_factory_object = NULL;
88
G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY)
91
gdm_local_display_factory_error_quark (void)
93
static GQuark ret = 0;
95
ret = g_quark_from_static_string ("gdm_local_display_factory_error");
102
listify_hash (gpointer key,
106
*list = g_list_prepend (*list, key);
110
sort_nums (gpointer a,
116
num_a = GPOINTER_TO_UINT (a);
117
num_b = GPOINTER_TO_UINT (b);
121
} else if (num_a < num_b) {
129
take_next_display_number (GdmLocalDisplayFactory *factory)
138
g_hash_table_foreach (factory->priv->displays, (GHFunc)listify_hash, &list);
143
/* sort low to high */
144
list = g_list_sort (list, (GCompareFunc)sort_nums);
146
g_debug ("GdmLocalDisplayFactory: Found the following X displays:");
147
for (l = list; l != NULL; l = l->next) {
148
g_debug ("GdmLocalDisplayFactory: %u", GPOINTER_TO_UINT (l->data));
151
for (l = list; l != NULL; l = l->next) {
153
num = GPOINTER_TO_UINT (l->data);
155
/* always fill zero */
156
if (l->prev == NULL && num != 0) {
160
/* now find the first hole */
161
if (l->next == NULL || GPOINTER_TO_UINT (l->next->data) != (num + 1)) {
168
/* now reserve this number */
169
g_debug ("GdmLocalDisplayFactory: Reserving X display: %u", ret);
170
g_hash_table_insert (factory->priv->displays, GUINT_TO_POINTER (ret), NULL);
176
on_display_disposed (GdmLocalDisplayFactory *factory,
179
g_debug ("GdmLocalDisplayFactory: Display %p disposed", display);
183
store_display (GdmLocalDisplayFactory *factory,
187
GdmDisplayStore *store;
189
g_signal_connect (display, "notify::status",
190
G_CALLBACK (on_display_status_changed), factory);
192
g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
194
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
195
gdm_display_store_add (store, display);
197
/* now fill our reserved spot */
198
g_hash_table_insert (factory->priv->displays, GUINT_TO_POINTER (num), NULL);
202
get_seat_of_transient_display (GdmLocalDisplayFactory *factory)
206
/* FIXME: don't hardcode seat */
208
seat_id = SYSTEMD_SEAT0_PATH;
210
seat_id = CK_SEAT1_PATH;
218
dbus-send --system --dest=org.gnome.DisplayManager \
219
--type=method_call --print-reply --reply-timeout=2000 \
220
/org/gnome/DisplayManager/Manager \
221
org.gnome.DisplayManager.Manager.GetDisplays
224
gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory,
233
g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
237
num = take_next_display_number (factory);
239
g_debug ("GdmLocalDisplayFactory: Creating transient display %d", num);
241
display = gdm_transient_display_new (num);
243
seat_id = get_seat_of_transient_display (factory);
244
g_object_set (display, "seat-id", seat_id, NULL);
246
store_display (factory, num, display);
248
if (! gdm_display_manage (display)) {
253
if (! gdm_display_get_id (display, id, NULL)) {
260
/* ref either held by store or not at all */
261
g_object_unref (display);
267
on_display_status_changed (GdmDisplay *display,
269
GdmLocalDisplayFactory *factory)
272
GdmDisplayStore *store;
274
char *seat_id = NULL;
275
gboolean is_initial = TRUE;
278
gdm_display_get_x11_display_number (display, &num, NULL);
279
g_assert (num != -1);
281
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
283
g_object_get (display, "seat-id", &seat_id, NULL);
284
g_object_get (display, "is-initial", &is_initial, NULL);
286
status = gdm_display_get_status (display);
288
g_debug ("GdmLocalDisplayFactory: display status changed: %d", status);
290
case GDM_DISPLAY_FINISHED:
291
/* remove the display number from factory->priv->displays
292
so that it may be reused */
293
g_hash_table_remove (factory->priv->displays, GUINT_TO_POINTER (num));
294
gdm_display_store_remove (store, display);
296
/* Create a new equivalent display if it was static */
297
if (GDM_IS_STATIC_DISPLAY (display)) {
298
/* reset num failures */
299
factory->priv->num_failures = 0;
301
create_display (factory, seat_id, is_initial);
304
case GDM_DISPLAY_FAILED:
305
/* leave the display number in factory->priv->displays
306
so that it doesn't get reused */
307
gdm_display_store_remove (store, display);
309
/* Create a new equivalent display if it was static */
310
if (GDM_IS_STATIC_DISPLAY (display)) {
312
factory->priv->num_failures++;
314
if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) {
316
g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors");
317
/* FIXME: should monitor hardware changes to
318
try again when seats change */
320
create_display (factory, seat_id, is_initial);
324
case GDM_DISPLAY_UNMANAGED:
326
case GDM_DISPLAY_PREPARED:
328
case GDM_DISPLAY_MANAGED:
331
g_assert_not_reached ();
339
lookup_by_seat_id (const char *id,
343
const char *looking_for = user_data;
347
g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL);
349
res = g_strcmp0 (current, looking_for) == 0;
357
create_display (GdmLocalDisplayFactory *factory,
361
GdmDisplayStore *store;
365
/* Ensure we don't create the same display more than once */
366
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
367
display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id);
368
if (display != NULL) {
372
g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id);
374
num = take_next_display_number (factory);
376
display = gdm_static_display_new (num);
378
g_object_set (display, "seat-id", seat_id, NULL);
379
g_object_set (display, "is-initial", initial, NULL);
381
store_display (factory, num, display);
383
/* let store own the ref */
384
g_object_unref (display);
386
if (! gdm_display_manage (display)) {
387
gdm_display_unmanage (display);
396
delete_display (GdmLocalDisplayFactory *factory,
397
const char *seat_id) {
399
GdmDisplayStore *store;
401
g_debug ("GdmLocalDisplayFactory: Removing displays on seat %s", seat_id);
403
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
404
gdm_display_store_foreach_remove (store, lookup_by_seat_id, (gpointer) seat_id);
407
static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory)
409
GError *error = NULL;
415
result = g_dbus_connection_call_sync (factory->priv->connection,
416
"org.freedesktop.login1",
417
"/org/freedesktop/login1",
418
"org.freedesktop.login1.Manager",
421
G_VARIANT_TYPE ("(a(so))"),
422
G_DBUS_CALL_FLAGS_NONE,
427
g_warning ("GdmLocalDisplayFactory: Failed to issue method call: %s", error->message);
428
g_clear_error (&error);
432
array = g_variant_get_child_value (result, 0);
433
g_variant_iter_init (&iter, array);
435
while (g_variant_iter_loop (&iter, "(&so)", &seat, NULL))
436
create_display (factory, seat, TRUE);
438
g_variant_unref (result);
439
g_variant_unref (array);
444
on_seat_new (GDBusConnection *connection,
445
const gchar *sender_name,
446
const gchar *object_path,
447
const gchar *interface_name,
448
const gchar *signal_name,
449
GVariant *parameters,
454
g_variant_get (parameters, "(&s&o)", &seat, NULL);
455
create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, FALSE);
459
on_seat_removed (GDBusConnection *connection,
460
const gchar *sender_name,
461
const gchar *object_path,
462
const gchar *interface_name,
463
const gchar *signal_name,
464
GVariant *parameters,
469
g_variant_get (parameters, "(&s&o)", &seat, NULL);
470
delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
474
gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
476
factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection,
477
"org.freedesktop.login1",
478
"org.freedesktop.login1.Manager",
480
"/org/freedesktop/login1",
482
G_DBUS_SIGNAL_FLAGS_NONE,
484
g_object_ref (factory),
486
factory->priv->seat_removed_id = g_dbus_connection_signal_subscribe (factory->priv->connection,
487
"org.freedesktop.login1",
488
"org.freedesktop.login1.Manager",
490
"/org/freedesktop/login1",
492
G_DBUS_SIGNAL_FLAGS_NONE,
494
g_object_ref (factory),
499
gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
501
if (factory->priv->seat_new_id) {
502
g_dbus_connection_signal_unsubscribe (factory->priv->connection,
503
factory->priv->seat_new_id);
504
factory->priv->seat_new_id = 0;
506
if (factory->priv->seat_removed_id) {
507
g_dbus_connection_signal_unsubscribe (factory->priv->connection,
508
factory->priv->seat_removed_id);
509
factory->priv->seat_removed_id = 0;
516
gdm_local_display_factory_start (GdmDisplayFactory *base_factory)
518
GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (base_factory);
521
g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
524
if (sd_booted () > 0) {
525
gdm_local_display_factory_start_monitor (factory);
526
return gdm_local_display_factory_sync_seats (factory);
530
/* On ConsoleKit just create Seat1, and that's it. */
531
display = create_display (factory, CK_SEAT1_PATH, TRUE);
533
return display != NULL;
537
gdm_local_display_factory_stop (GdmDisplayFactory *base_factory)
539
GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (base_factory);
541
g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
544
gdm_local_display_factory_stop_monitor (factory);
551
gdm_local_display_factory_set_property (GObject *object,
558
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
564
gdm_local_display_factory_get_property (GObject *object,
571
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
577
handle_create_transient_display (GdmDBusLocalDisplayFactory *skeleton,
578
GDBusMethodInvocation *invocation,
579
GdmLocalDisplayFactory *factory)
581
GError *error = NULL;
585
created = gdm_local_display_factory_create_transient_display (factory,
589
g_dbus_method_invocation_return_gerror (invocation, error);
591
gdm_dbus_local_display_factory_complete_create_transient_display (skeleton, invocation, id);
599
register_factory (GdmLocalDisplayFactory *factory)
601
GError *error = NULL;
604
factory->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
605
if (factory->priv->connection == NULL) {
606
g_critical ("error getting system bus: %s", error->message);
607
g_error_free (error);
611
factory->priv->skeleton = GDM_DBUS_LOCAL_DISPLAY_FACTORY (gdm_dbus_local_display_factory_skeleton_new ());
613
g_signal_connect (factory->priv->skeleton,
614
"handle-create-transient-display",
615
G_CALLBACK (handle_create_transient_display),
618
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (factory->priv->skeleton),
619
factory->priv->connection,
620
GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH,
622
g_critical ("error exporting LocalDisplayFactory object: %s", error->message);
623
g_error_free (error);
631
gdm_local_display_factory_constructor (GType type,
632
guint n_construct_properties,
633
GObjectConstructParam *construct_properties)
635
GdmLocalDisplayFactory *factory;
638
factory = GDM_LOCAL_DISPLAY_FACTORY (G_OBJECT_CLASS (gdm_local_display_factory_parent_class)->constructor (type,
639
n_construct_properties,
640
construct_properties));
642
res = register_factory (factory);
644
g_warning ("Unable to register local display factory with system bus");
647
return G_OBJECT (factory);
651
gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass)
653
GObjectClass *object_class = G_OBJECT_CLASS (klass);
654
GdmDisplayFactoryClass *factory_class = GDM_DISPLAY_FACTORY_CLASS (klass);
656
object_class->get_property = gdm_local_display_factory_get_property;
657
object_class->set_property = gdm_local_display_factory_set_property;
658
object_class->finalize = gdm_local_display_factory_finalize;
659
object_class->constructor = gdm_local_display_factory_constructor;
661
factory_class->start = gdm_local_display_factory_start;
662
factory_class->stop = gdm_local_display_factory_stop;
664
g_type_class_add_private (klass, sizeof (GdmLocalDisplayFactoryPrivate));
668
gdm_local_display_factory_init (GdmLocalDisplayFactory *factory)
670
factory->priv = GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE (factory);
672
factory->priv->displays = g_hash_table_new (NULL, NULL);
676
gdm_local_display_factory_finalize (GObject *object)
678
GdmLocalDisplayFactory *factory;
680
g_return_if_fail (object != NULL);
681
g_return_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (object));
683
factory = GDM_LOCAL_DISPLAY_FACTORY (object);
685
g_return_if_fail (factory->priv != NULL);
687
g_clear_object (&factory->priv->connection);
688
g_clear_object (&factory->priv->skeleton);
690
g_hash_table_destroy (factory->priv->displays);
693
gdm_local_display_factory_stop_monitor (factory);
696
G_OBJECT_CLASS (gdm_local_display_factory_parent_class)->finalize (object);
699
GdmLocalDisplayFactory *
700
gdm_local_display_factory_new (GdmDisplayStore *store)
702
if (local_display_factory_object != NULL) {
703
g_object_ref (local_display_factory_object);
705
local_display_factory_object = g_object_new (GDM_TYPE_LOCAL_DISPLAY_FACTORY,
706
"display-store", store,
708
g_object_add_weak_pointer (local_display_factory_object,
709
(gpointer *) &local_display_factory_object);
712
return GDM_LOCAL_DISPLAY_FACTORY (local_display_factory_object);