]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/seat-unity.c
Use to set_next_session seat class method in SeatUnity to tell the compositor about...
[sojka/lightdm.git] / src / seat-unity.c
1 /*
2  * Copyright (C) 2012-2013 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <string.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 #include <glib/gstdio.h>
16
17 #include "seat-unity.h"
18 #include "configuration.h"
19 #include "x-server-local.h"
20 #include "mir-server.h"
21 #include "vt.h"
22 #include "plymouth.h"
23
24 typedef enum
25 {
26    USC_MESSAGE_PING = 0,
27    USC_MESSAGE_PONG = 1,
28    USC_MESSAGE_READY = 2,
29    USC_MESSAGE_SESSION_CONNECTED = 3,
30    USC_MESSAGE_SET_ACTIVE_SESSION = 4,
31    USC_MESSAGE_SET_NEXT_SESSION = 5,
32 } USCMessageID;
33
34 struct SeatUnityPrivate
35 {
36     /* VT we are running on */
37     gint vt;
38
39     /* File to log to */
40     gchar *log_file;
41
42     /* Filename of Mir socket */
43     gchar *mir_socket_filename;
44
45     /* Pipes to communicate with compositor */
46     int to_compositor_pipe[2];
47     int from_compositor_pipe[2];
48
49     /* IO channel listening on for messages from the compositor */
50     GIOChannel *from_compositor_channel;
51
52     /* TRUE when the compositor indicates it is ready */
53     gboolean compositor_ready;
54
55     /* Buffer reading from channel */
56     guint8 *read_buffer;
57     gsize read_buffer_length;
58     gsize read_buffer_n_used;
59
60     /* Compositor process */
61     Process *compositor_process;
62
63     /* Timeout when waiting for compositor to start */
64     guint compositor_timeout;
65
66     /* Next Mir ID to use for a compositor client */
67     gint next_id;
68
69     /* TRUE if using VT switching fallback */
70     gboolean use_vt_switching;
71
72     /* The currently visible session */
73     Session *active_session;
74     DisplayServer *active_display_server;
75 };
76
77 G_DEFINE_TYPE (SeatUnity, seat_unity, SEAT_TYPE);
78
79 static gboolean
80 seat_unity_get_start_local_sessions (Seat *seat)
81 {
82     return !seat_get_string_property (seat, "xdmcp-manager");
83 }
84
85 static void
86 seat_unity_setup (Seat *seat)
87 {
88     seat_set_can_switch (seat, TRUE);
89     SEAT_CLASS (seat_unity_parent_class)->setup (seat);
90 }
91
92 static void
93 compositor_stopped_cb (Process *process, SeatUnity *seat)
94 {
95     if (seat->priv->compositor_timeout != 0)
96         g_source_remove (seat->priv->compositor_timeout);
97     seat->priv->compositor_timeout = 0;
98
99     /* Finished with the VT */
100     vt_unref (seat->priv->vt);
101     seat->priv->vt = -1;
102
103     if (seat_get_is_stopping (SEAT (seat)))
104     {
105         SEAT_CLASS (seat_unity_parent_class)->stop (SEAT (seat));
106         return;
107     }
108
109     /* If stopped before it was ready, then revert to VT mode */
110     if (!seat->priv->compositor_ready)
111     {
112         g_debug ("Compositor failed to start, switching to VT mode");
113         seat->priv->use_vt_switching = TRUE;
114         SEAT_CLASS (seat_unity_parent_class)->start (SEAT (seat));
115         return;
116     }
117
118     g_debug ("Stopping Unity seat, compositor terminated");
119
120     seat_stop (SEAT (seat));
121 }
122
123 static void
124 compositor_run_cb (Process *process, SeatUnity *seat)
125 {
126     int fd;
127
128     /* Make input non-blocking */
129     fd = open ("/dev/null", O_RDONLY);
130     dup2 (fd, STDIN_FILENO);
131     close (fd);
132
133     /* Redirect output to logfile */
134     if (seat->priv->log_file)
135     {
136          int fd;
137
138          fd = g_open (seat->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
139          if (fd < 0)
140              g_warning ("Failed to open log file %s: %s", seat->priv->log_file, g_strerror (errno));
141          else
142          {
143              dup2 (fd, STDOUT_FILENO);
144              dup2 (fd, STDERR_FILENO);
145              close (fd);
146          }
147     }
148 }
149
150 static void
151 write_message (SeatUnity *seat, guint16 id, const guint8 *payload, guint16 payload_length)
152 {
153     guint8 *data;
154     gsize data_length = 4 + payload_length;
155
156     data = g_malloc (data_length);
157     data[0] = id >> 8;
158     data[1] = id & 0xFF;
159     data[2] = payload_length >> 8;
160     data[3] = payload_length & 0xFF;
161     memcpy (data + 4, payload, payload_length);
162
163     errno = 0;
164     if (write (seat->priv->to_compositor_pipe[1], data, data_length) != data_length)
165         g_warning ("Failed to write to compositor: %s", strerror (errno));
166 }
167
168 static gboolean
169 read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
170 {
171     SeatUnity *seat = data;
172     gsize n_to_read = 0;
173     guint16 id, payload_length;
174     /*guint8 *payload;*/
175   
176     if (condition == G_IO_HUP)
177     {
178         g_debug ("Compositor closed communication channel");
179         return FALSE;
180     }
181
182     /* Work out how much required for a message */
183     if (seat->priv->read_buffer_n_used < 4)
184         n_to_read = 4 - seat->priv->read_buffer_n_used;
185     else
186     {
187         payload_length = seat->priv->read_buffer[2] << 8 | seat->priv->read_buffer[3];
188         n_to_read = 4 + payload_length - seat->priv->read_buffer_n_used;
189     }
190
191     /* Read from compositor */
192     if (n_to_read > 0)
193     {
194         gsize n_total, n_read = 0;
195         GIOStatus status;
196         GError *error = NULL;
197
198         n_total = seat->priv->read_buffer_n_used + n_to_read;
199         if (seat->priv->read_buffer_length < n_total)
200             seat->priv->read_buffer = g_realloc (seat->priv->read_buffer, n_total);
201
202         status = g_io_channel_read_chars (source,
203                                           (gchar *)seat->priv->read_buffer + seat->priv->read_buffer_n_used,
204                                           n_to_read,
205                                           &n_read,
206                                           &error);
207         if (error)
208             g_warning ("Failed to read from compositor: %s", error->message);
209         if (status != G_IO_STATUS_NORMAL)
210             return TRUE;
211         g_clear_error (&error);
212         seat->priv->read_buffer_n_used += n_read;
213     }
214
215     /* Read header */
216     if (seat->priv->read_buffer_n_used < 4)
217          return TRUE;
218     id = seat->priv->read_buffer[0] << 8 | seat->priv->read_buffer[1];
219     payload_length = seat->priv->read_buffer[2] << 8 | seat->priv->read_buffer[3];
220
221     /* Read payload */
222     if (seat->priv->read_buffer_n_used < 4 + payload_length)
223         return TRUE;
224     /*payload = seat->priv->read_buffer + 4;*/
225
226     switch (id)
227     {
228     case USC_MESSAGE_PING:
229         g_debug ("PING!");
230         write_message (seat, USC_MESSAGE_PONG, NULL, 0);
231         break;
232     case USC_MESSAGE_PONG:
233         g_debug ("PONG!");
234         break;
235     case USC_MESSAGE_READY:
236         g_debug ("READY");
237         if (!seat->priv->compositor_ready)
238         {
239             seat->priv->compositor_ready = TRUE;
240             g_debug ("Compositor ready");
241             g_source_remove (seat->priv->compositor_timeout);
242             seat->priv->compositor_timeout = 0;
243             SEAT_CLASS (seat_unity_parent_class)->start (SEAT (seat));
244         }
245         break;
246     case USC_MESSAGE_SESSION_CONNECTED:
247         g_debug ("SESSION CONNECTED");
248         break;
249     default:
250         g_warning ("Ingoring unknown message %d with %d octets from system compositor", id, payload_length);
251         break;
252     }
253
254     /* Clear buffer */
255     seat->priv->read_buffer_n_used = 0;
256
257     return TRUE;
258 }
259
260 static gchar *
261 get_absolute_command (const gchar *command)
262 {
263     gchar **tokens;
264     gchar *absolute_binary, *absolute_command = NULL;
265
266     tokens = g_strsplit (command, " ", 2);
267
268     absolute_binary = g_find_program_in_path (tokens[0]);
269     if (absolute_binary)
270     {
271         if (tokens[1])
272             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
273         else
274             absolute_command = g_strdup (absolute_binary);
275         g_free (absolute_binary);
276     }
277     else
278         absolute_command = g_strdup (command);
279
280     g_strfreev (tokens);
281
282     return absolute_command;
283 }
284
285 static gboolean
286 compositor_timeout_cb (gpointer data)
287 {
288     SeatUnity *seat = data;
289
290     /* Stop the compositor - it is not working */
291     process_stop (seat->priv->compositor_process);
292
293     return TRUE;
294 }
295
296 static gboolean
297 seat_unity_start (Seat *seat)
298 {
299     const gchar *compositor_command;
300     gchar *command, *absolute_command, *dir;
301     gboolean result;
302     int timeout;
303
304     /* Replace Plymouth if it is running */
305     if (plymouth_get_is_active () && plymouth_has_active_vt ())
306     {
307         gint active_vt = vt_get_active ();
308         if (active_vt >= vt_get_min ())
309         {
310             SEAT_UNITY (seat)->priv->vt = active_vt;
311             plymouth_quit (TRUE);
312         }
313         else
314             g_debug ("Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
315     }
316     if (plymouth_get_is_active ())
317         plymouth_quit (FALSE);
318     if (SEAT_UNITY (seat)->priv->vt < 0)
319         SEAT_UNITY (seat)->priv->vt = vt_get_unused ();
320     if (SEAT_UNITY (seat)->priv->vt < 0)
321     {
322         g_debug ("Failed to get a VT to run on");
323         return FALSE;
324     }
325     vt_ref (SEAT_UNITY (seat)->priv->vt);
326
327     /* Create pipes to talk to compositor */
328     if (pipe (SEAT_UNITY (seat)->priv->to_compositor_pipe) < 0 || pipe (SEAT_UNITY (seat)->priv->from_compositor_pipe) < 0)
329     {
330         g_debug ("Failed to create compositor pipes: %s", g_strerror (errno));
331         return FALSE;
332     }
333
334     /* Don't allow the daemon end of the pipes to be accessed in the compositor */
335     fcntl (SEAT_UNITY (seat)->priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC);
336     fcntl (SEAT_UNITY (seat)->priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC);
337
338     /* Listen for messages from the compositor */
339     SEAT_UNITY (seat)->priv->from_compositor_channel = g_io_channel_unix_new (SEAT_UNITY (seat)->priv->from_compositor_pipe[0]);
340     g_io_add_watch (SEAT_UNITY (seat)->priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, seat);
341
342     /* Setup logging */
343     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
344     SEAT_UNITY (seat)->priv->log_file = g_build_filename (dir, "unity-system-compositor.log", NULL);
345     g_debug ("Logging to %s", SEAT_UNITY (seat)->priv->log_file);
346     g_free (dir);
347
348     SEAT_UNITY (seat)->priv->mir_socket_filename = g_strdup ("/tmp/mir_socket"); // FIXME: Use this socket by default as XMir is hardcoded to this
349     timeout = seat_get_integer_property (seat, "unity-compositor-timeout");
350     compositor_command = seat_get_string_property (seat, "unity-compositor-command");
351     command = g_strdup_printf ("%s --from-dm-fd %d --to-dm-fd %d --vt %d", compositor_command, SEAT_UNITY (seat)->priv->to_compositor_pipe[0], SEAT_UNITY (seat)->priv->from_compositor_pipe[1], SEAT_UNITY (seat)->priv->vt);
352
353     absolute_command = get_absolute_command (command);
354     g_free (command);
355
356     /* Start the compositor */
357     process_set_command (SEAT_UNITY (seat)->priv->compositor_process, absolute_command);
358     g_free (absolute_command);
359     g_signal_connect (SEAT_UNITY (seat)->priv->compositor_process, "stopped", G_CALLBACK (compositor_stopped_cb), seat);
360     g_signal_connect (SEAT_UNITY (seat)->priv->compositor_process, "run", G_CALLBACK (compositor_run_cb), seat);
361     result = process_start (SEAT_UNITY (seat)->priv->compositor_process, FALSE);
362
363     /* Close compostor ends of the pipes */
364     close (SEAT_UNITY (seat)->priv->to_compositor_pipe[0]);
365     SEAT_UNITY (seat)->priv->to_compositor_pipe[0] = 0;
366     close (SEAT_UNITY (seat)->priv->from_compositor_pipe[1]);
367     SEAT_UNITY (seat)->priv->from_compositor_pipe[1] = 0;
368
369     if (!result)
370         return FALSE;
371
372     /* Connect to the compositor */
373     timeout = seat_get_integer_property (seat, "unity-compositor-timeout");
374     if (timeout <= 0)
375         timeout = 60;
376     g_debug ("Waiting for system compositor for %ds", timeout);
377     SEAT_UNITY (seat)->priv->compositor_timeout = g_timeout_add (timeout * 1000, compositor_timeout_cb, seat);
378
379     return TRUE;
380 }
381
382 static DisplayServer *
383 create_x_server (Seat *seat)
384 {
385     XServerLocal *x_server;
386     const gchar *command = NULL, *layout = NULL, *config_file = NULL, *xdmcp_manager = NULL, *key_name = NULL;
387     gboolean allow_tcp;
388     gint port = 0;
389
390     g_debug ("Starting X server on Unity compositor");
391
392     x_server = x_server_local_new ();
393
394     command = seat_get_string_property (seat, "xserver-command");
395     if (command)
396         x_server_local_set_command (x_server, command);
397
398     if (SEAT_UNITY (seat)->priv->use_vt_switching)
399         x_server_local_set_vt (x_server, vt_get_unused ());
400     else
401     {
402         gchar *id;
403
404         id = g_strdup_printf ("%d", SEAT_UNITY (seat)->priv->next_id);
405         SEAT_UNITY (seat)->priv->next_id++;
406         x_server_local_set_mir_id (x_server, id);
407         x_server_local_set_mir_socket (x_server, SEAT_UNITY (seat)->priv->mir_socket_filename);
408         g_free (id);
409     }
410
411     layout = seat_get_string_property (seat, "xserver-layout");
412     if (layout)
413         x_server_local_set_layout (x_server, layout);
414
415     config_file = seat_get_string_property (seat, "xserver-config");
416     if (config_file)
417         x_server_local_set_config (x_server, config_file);
418
419     allow_tcp = seat_get_boolean_property (seat, "xserver-allow-tcp");
420     x_server_local_set_allow_tcp (x_server, allow_tcp);
421
422     xdmcp_manager = seat_get_string_property (seat, "xdmcp-manager");
423     if (xdmcp_manager)
424         x_server_local_set_xdmcp_server (x_server, xdmcp_manager);
425
426     port = seat_get_integer_property (seat, "xdmcp-port");
427     if (port > 0)
428         x_server_local_set_xdmcp_port (x_server, port);
429
430     key_name = seat_get_string_property (seat, "xdmcp-key");
431     if (key_name)
432     {
433         gchar *dir, *path;
434         GKeyFile *keys;
435         gboolean result;
436         GError *error = NULL;
437
438         dir = config_get_string (config_get_instance (), "LightDM", "config-directory");
439         path = g_build_filename (dir, "keys.conf", NULL);
440         g_free (dir);
441
442         keys = g_key_file_new ();
443         result = g_key_file_load_from_file (keys, path, G_KEY_FILE_NONE, &error);
444         if (error)
445             g_debug ("Error getting key %s", error->message);
446         g_clear_error (&error);
447
448         if (result)
449         {
450             gchar *key = NULL;
451
452             if (g_key_file_has_key (keys, "keyring", key_name, NULL))
453                 key = g_key_file_get_string (keys, "keyring", key_name, NULL);
454             else
455                 g_debug ("Key %s not defined", key_name);
456
457             if (key)
458                 x_server_local_set_xdmcp_key (x_server, key);
459             g_free (key);
460         }
461
462         g_free (path);
463         g_key_file_free (keys);
464     }
465
466     return DISPLAY_SERVER (x_server);
467 }
468
469 static DisplayServer *
470 create_mir_server (Seat *seat)
471 {
472     MirServer *mir_server;
473
474     mir_server = mir_server_new ();
475     mir_server_set_parent_socket (mir_server, SEAT_UNITY (seat)->priv->mir_socket_filename);
476
477     if (SEAT_UNITY (seat)->priv->use_vt_switching)
478         mir_server_set_vt (mir_server, vt_get_unused ());
479     else
480     {
481         gchar *id;
482
483         id = g_strdup_printf ("%d", SEAT_UNITY (seat)->priv->next_id);
484         SEAT_UNITY (seat)->priv->next_id++;
485         mir_server_set_id (mir_server, id);
486         mir_server_set_parent_socket (mir_server, SEAT_UNITY (seat)->priv->mir_socket_filename);
487         g_free (id);
488     }   
489
490     return DISPLAY_SERVER (mir_server);
491 }
492
493 static DisplayServer *
494 seat_unity_create_display_server (Seat *seat, const gchar *session_type)
495 {  
496     if (strcmp (session_type, "x") == 0)
497         return create_x_server (seat);
498     else if (strcmp (session_type, "mir") == 0)
499         return create_mir_server (seat);
500     else
501     {
502         g_warning ("Can't create unsupported display server '%s'", session_type);
503         return NULL;
504     }
505 }
506
507 static Greeter *
508 seat_unity_create_greeter_session (Seat *seat)
509 {
510     Greeter *greeter_session;
511
512     greeter_session = SEAT_CLASS (seat_unity_parent_class)->create_greeter_session (seat);
513     session_set_env (SESSION (greeter_session), "XDG_SEAT", "seat0");
514     if (!SEAT_UNITY (seat)->priv->use_vt_switching)
515     {
516         gchar *value = g_strdup_printf ("%d", SEAT_UNITY (seat)->priv->vt);
517         session_set_env (SESSION (greeter_session), "XDG_VTNR", value);
518         g_free (value);
519     }
520
521     return greeter_session;
522 }
523
524 static Session *
525 seat_unity_create_session (Seat *seat, Session *user_session)
526 {
527     Session *session;
528
529     session = SEAT_CLASS (seat_unity_parent_class)->create_session (seat, user_session);
530     session_set_env (session, "XDG_SEAT", "seat0");
531     if (!SEAT_UNITY (seat)->priv->use_vt_switching)
532     {
533         gchar *value = g_strdup_printf ("%d", SEAT_UNITY (seat)->priv->vt);
534         session_set_env (SESSION (session), "XDG_VTNR", value);
535         g_free (value);
536     }
537
538     return session;
539 }
540
541 static void
542 seat_unity_set_active_session (Seat *seat, Session *session)
543 {
544     DisplayServer *display_server;
545
546     /* If no compositor, have to use VT switching */
547     if (SEAT_UNITY (seat)->priv->use_vt_switching)
548     {
549         gint vt = display_server_get_vt (session_get_display_server (session));
550         if (vt >= 0)
551             vt_set_active (vt);
552
553         SEAT_CLASS (seat_unity_parent_class)->set_active_session (seat, session);
554         return;
555     }
556
557     if (session == SEAT_UNITY (seat)->priv->active_session)
558         return;
559     SEAT_UNITY (seat)->priv->active_session = g_object_ref (session);
560
561     display_server = session_get_display_server (session);
562     if (SEAT_UNITY (seat)->priv->active_display_server != display_server)
563     {
564         const gchar *id = NULL;
565
566         SEAT_UNITY (seat)->priv->active_display_server = g_object_ref (display_server);
567
568         if (IS_X_SERVER_LOCAL (display_server))
569             id = x_server_local_get_mir_id (X_SERVER_LOCAL (display_server));
570         else if (IS_MIR_SERVER (display_server))
571             id = mir_server_get_id (MIR_SERVER (display_server));
572
573         if (id)
574         {
575             g_debug ("Switching to Mir session %s", id);
576             write_message (SEAT_UNITY (seat), USC_MESSAGE_SET_ACTIVE_SESSION, (const guint8 *) id, strlen (id));
577         }
578         else
579             g_warning ("Failed to work out session ID");
580     }
581
582     SEAT_CLASS (seat_unity_parent_class)->set_active_session (seat, session);
583 }
584
585 static Session *
586 seat_unity_get_active_session (Seat *seat)
587 {
588     if (SEAT_UNITY (seat)->priv->use_vt_switching)
589     {
590         gint vt;
591         GList *link;
592         vt = vt_get_active ();
593         if (vt < 0)
594             return NULL;
595
596         for (link = seat_get_sessions (seat); link; link = link->next)
597         {
598             Session *session = link->data;
599             if (display_server_get_vt (session_get_display_server (session)) == vt)
600                 return session;
601         }
602
603         return NULL;
604     }
605
606     return SEAT_UNITY (seat)->priv->active_session;
607 }
608
609 static void
610 seat_unity_set_next_session (Seat *seat, Session *session)
611 {
612     DisplayServer *display_server;
613     const gchar *id = NULL;
614
615     /* If no compositor, don't worry about it */
616     if (SEAT_UNITY (seat)->priv->use_vt_switching)
617         return;
618
619     display_server = session_get_display_server (session);
620     if (IS_MIR_SERVER (display_server))
621     {
622         id = mir_server_get_id (MIR_SERVER (display_server));
623
624         if (id)
625         {
626             g_debug ("Marking Mir session %s as the next session", id);
627             write_message (SEAT_UNITY (seat), USC_MESSAGE_SET_NEXT_SESSION, (const guint8 *) id, strlen (id));
628         }
629         else
630         {
631             g_warning ("Failed to work out session ID to mark");
632         }
633     }
634 }
635
636 static void
637 seat_unity_run_script (Seat *seat, DisplayServer *display_server, Process *script)
638 {
639     const gchar *path;
640     XServerLocal *x_server;
641
642     x_server = X_SERVER_LOCAL (display_server);
643     path = x_server_local_get_authority_file_path (x_server);
644     process_set_env (script, "DISPLAY", x_server_get_address (X_SERVER (x_server)));
645     process_set_env (script, "XAUTHORITY", path);
646
647     SEAT_CLASS (seat_unity_parent_class)->run_script (seat, display_server, script);
648 }
649
650 static void
651 seat_unity_stop (Seat *seat)
652 {
653     /* Stop the compositor first */
654     if (process_get_is_running (SEAT_UNITY (seat)->priv->compositor_process))
655     {
656         process_stop (SEAT_UNITY (seat)->priv->compositor_process);
657         return;
658     }
659
660     SEAT_CLASS (seat_unity_parent_class)->stop (seat);
661 }
662
663 static void
664 seat_unity_init (SeatUnity *seat)
665 {
666     seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_UNITY_TYPE, SeatUnityPrivate);
667     seat->priv->vt = -1;
668     seat->priv->compositor_process = process_new ();
669 }
670
671 static void
672 seat_unity_finalize (GObject *object)
673 {
674     SeatUnity *seat = SEAT_UNITY (object);
675
676     if (seat->priv->vt >= 0)
677         vt_unref (seat->priv->vt);
678     g_free (seat->priv->log_file);
679     g_free (seat->priv->mir_socket_filename);
680     close (seat->priv->to_compositor_pipe[0]);
681     close (seat->priv->to_compositor_pipe[1]);
682     close (seat->priv->from_compositor_pipe[0]);
683     close (seat->priv->from_compositor_pipe[1]);
684     g_io_channel_unref (seat->priv->from_compositor_channel);
685     g_free (seat->priv->read_buffer);
686     g_object_unref (seat->priv->compositor_process);
687     if (seat->priv->active_session)
688         g_object_unref (seat->priv->active_session);
689     if (seat->priv->active_display_server)
690         g_object_unref (seat->priv->active_display_server);
691
692     G_OBJECT_CLASS (seat_unity_parent_class)->finalize (object);
693 }
694
695 static void
696 seat_unity_class_init (SeatUnityClass *klass)
697 {
698     GObjectClass *object_class = G_OBJECT_CLASS (klass);
699     SeatClass *seat_class = SEAT_CLASS (klass);
700
701     object_class->finalize = seat_unity_finalize;
702     seat_class->get_start_local_sessions = seat_unity_get_start_local_sessions;
703     seat_class->setup = seat_unity_setup;
704     seat_class->start = seat_unity_start;
705     seat_class->create_display_server = seat_unity_create_display_server;
706     seat_class->create_greeter_session = seat_unity_create_greeter_session;
707     seat_class->create_session = seat_unity_create_session;
708     seat_class->set_active_session = seat_unity_set_active_session;
709     seat_class->get_active_session = seat_unity_get_active_session;
710     seat_class->set_next_session = seat_unity_set_next_session;
711     seat_class->run_script = seat_unity_run_script;
712     seat_class->stop = seat_unity_stop;
713
714     g_type_class_add_private (klass, sizeof (SeatUnityPrivate));
715 }