1 /* Unique - Single Instance Backendlication library
2  * uniquebackend.c: Base class for IPC transports
3  *
4  * Copyright (C) 2007  Emmanuele Bassi  <ebassi@o-hand.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  */
21 
22 /**
23  * SECTION:unique-backend
24  * @short_description: Backend abstraction
25  *
26  * #UniqueBackend is the base, abstract class implemented by the different
27  * IPC mechanisms used by Unique. Each #UniqueApp instance creates a
28  * #UniqueBackend to request the name or to send messages.
29  */
30 
31 #include <config.h>
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <time.h>
37 
38 #include <glib/gi18n.h>
39 
40 #include <gdk/gdk.h>
41 #ifdef GDK_WINDOWING_X11
42 #include <gdk/gdkx.h>
43 #include <X11/Xatom.h>
44 #endif
45 
46 #include "uniquebackend.h"
47 #include "uniqueinternals.h"
48 
49 G_DEFINE_ABSTRACT_TYPE (UniqueBackend, unique_backend, G_TYPE_OBJECT);
50 
51 static void
unique_backend_finalize(GObject * gobject)52 unique_backend_finalize (GObject *gobject)
53 {
54   UniqueBackend *backend = UNIQUE_BACKEND (gobject);
55 
56   g_free (backend->name);
57   g_free (backend->startup_id);
58 
59   G_OBJECT_CLASS (unique_backend_parent_class)->finalize (gobject);
60 }
61 
62 static void
unique_backend_class_init(UniqueBackendClass * klass)63 unique_backend_class_init (UniqueBackendClass *klass)
64 {
65   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
66 
67   gobject_class->finalize = unique_backend_finalize;
68 }
69 
70 static void
unique_backend_init(UniqueBackend * backend)71 unique_backend_init (UniqueBackend *backend)
72 {
73   backend->name = NULL;
74   backend->startup_id = NULL;
75   backend->screen = gdk_screen_get_default ();
76   backend->workspace = -1;
77 }
78 
79 /**
80  * unique_backend_set_name:
81  * @backend: FIXME
82  * @name: FIXME
83  *
84  * FIXME
85  */
86 void
unique_backend_set_name(UniqueBackend * backend,const gchar * name)87 unique_backend_set_name (UniqueBackend *backend,
88                          const gchar   *name)
89 {
90   g_return_if_fail (UNIQUE_IS_BACKEND (backend));
91   g_return_if_fail (name != NULL);
92 
93   if (!backend->name)
94     {
95       backend->name = g_strdup (name);
96       return;
97     }
98 
99   if (strcmp (backend->name, name) != 0)
100     {
101       g_free (backend->name);
102       backend->name = g_strdup (name);
103     }
104 }
105 
106 /**
107  * unique_backend_get_name:
108  * @backend: FIXME
109  *
110  * FIXME
111  *
112  * Return value: FIXME
113  */
114 const gchar *
unique_backend_get_name(UniqueBackend * backend)115 unique_backend_get_name (UniqueBackend *backend)
116 {
117   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), NULL);
118 
119   return backend->name;
120 }
121 
122 /**
123  * unique_backend_set_startup_id:
124  * @backend: FIXME
125  * @startup_id: FIXME
126  *
127  * FIXME
128  */
129 void
unique_backend_set_startup_id(UniqueBackend * backend,const gchar * startup_id)130 unique_backend_set_startup_id (UniqueBackend *backend,
131                                const gchar   *startup_id)
132 {
133   g_return_if_fail (UNIQUE_IS_BACKEND (backend));
134   g_return_if_fail (startup_id != NULL);
135 
136   if (!backend->startup_id)
137     {
138       backend->startup_id = g_strdup (startup_id);
139       return;
140     }
141 
142   if (strcmp (backend->startup_id, startup_id) != 0)
143     {
144       g_free (backend->startup_id);
145       backend->startup_id = g_strdup (startup_id);
146     }
147 }
148 
149 /**
150  * unique_backend_get_startup_id:
151  * @backend: FIXME
152  *
153  * FIXME
154  *
155  * Return value: FIXME
156  */
157 const gchar *
unique_backend_get_startup_id(UniqueBackend * backend)158 unique_backend_get_startup_id (UniqueBackend *backend)
159 {
160   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), NULL);
161 
162   return backend->startup_id;
163 }
164 
165 /**
166  * unique_backend_set_screen:
167  * @backend: FIXME
168  * @screen: FIXME
169  *
170  * FIXME
171  */
172 void
unique_backend_set_screen(UniqueBackend * backend,GdkScreen * screen)173 unique_backend_set_screen (UniqueBackend *backend,
174                            GdkScreen     *screen)
175 {
176   g_return_if_fail (UNIQUE_IS_BACKEND (backend));
177   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
178 
179   if (!screen)
180     screen = gdk_screen_get_default ();
181 
182   backend->screen = screen;
183 }
184 
185 /**
186  * unique_backend_get_screen:
187  * @backend: FIXME
188  *
189  * FIXME
190  *
191  * Return value: FIXME
192  */
193 GdkScreen *
unique_backend_get_screen(UniqueBackend * backend)194 unique_backend_get_screen (UniqueBackend *backend)
195 {
196   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), NULL);
197 
198   return backend->screen;
199 }
200 
201 /**
202  * unique_backend_get_workspace:
203  * @backend: a #UniqueBackend
204  *
205  * Retrieves the current workspace.
206  *
207  * Return value: a workspace number
208  */
209 guint
unique_backend_get_workspace(UniqueBackend * backend)210 unique_backend_get_workspace (UniqueBackend *backend)
211 {
212   GdkDisplay *display;
213   GdkWindow *root_win;
214 #ifdef GDK_WINDOWING_X11
215   Atom _net_current_desktop, type;
216   int format;
217   unsigned long n_items, bytes_after;
218   unsigned char *data_return = 0;
219 #endif
220 
221   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), 0);
222 
223   if (backend->workspace != -1)
224     return backend->workspace;
225 
226   display = gdk_screen_get_display (backend->screen);
227   root_win = gdk_screen_get_root_window (backend->screen);
228 
229 #ifdef GDK_WINDOWING_X11
230   _net_current_desktop =
231     gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP");
232 
233   XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
234                       GDK_WINDOW_XID (root_win),
235                       _net_current_desktop,
236                       0, G_MAXLONG,
237                       False, XA_CARDINAL,
238                       &type, &format, &n_items, &bytes_after,
239                       &data_return);
240 
241   if (type == XA_CARDINAL && format == 32 && n_items > 0)
242     {
243       backend->workspace = (guint) data_return[0];
244       XFree (data_return);
245     }
246 #endif
247 
248   return backend->workspace;
249 }
250 
251 /**
252  * unique_backend_request_name:
253  * @backend: a #UniqueBackend
254  *
255  * Requests the name set using unique_backend_set_name() using @backend.
256  *
257  * Return value: %TRUE if the name was assigned to us, %FALSE if there
258  *   already is a registered name
259  */
260 gboolean
unique_backend_request_name(UniqueBackend * backend)261 unique_backend_request_name (UniqueBackend *backend)
262 {
263   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), FALSE);
264 
265   return UNIQUE_BACKEND_GET_CLASS (backend)->request_name (backend);
266 }
267 
268 /**
269  * unique_backend_send_message:
270  * @backend: a #UniqueBackend
271  * @command_id: command to send
272  * @message_data: message to send, or %NULL
273  * @time_: time of the command emission, or 0 for the current time
274  *
275  * Sends @command_id, and optionally @message_data, to a running instance
276  * using @backend.
277  *
278  * Return value: a #UniqueResponse value sent by the running instance
279  */
280 UniqueResponse
unique_backend_send_message(UniqueBackend * backend,gint command_id,UniqueMessageData * message_data,guint time_)281 unique_backend_send_message (UniqueBackend     *backend,
282                              gint               command_id,
283                              UniqueMessageData *message_data,
284                              guint              time_)
285 {
286   g_return_val_if_fail (UNIQUE_IS_BACKEND (backend), UNIQUE_RESPONSE_INVALID);
287   g_return_val_if_fail (command_id != 0, UNIQUE_RESPONSE_INVALID);
288 
289   if (time_ == 0)
290     time_ = (guint) time (NULL);
291 
292   return UNIQUE_BACKEND_GET_CLASS (backend)->send_message (backend, command_id,
293                                                            message_data,
294                                                            time_);
295 }
296 
297 #include "bacon/uniquebackend-bacon.h"
298 #ifdef HAVE_DBUS
299 #include "dbus/uniquebackend-dbus.h"
300 #endif
301 
302 /**
303  * unique_backend_create:
304  *
305  * Creates a #UniqueBackend using the default backend defined at
306  * compile time. You can override the default backend by setting the
307  * <literal>UNIQUE_BACKEND</literal> environment variable with the
308  * name of the desired backend.
309  *
310  * Return value: the newly created #UniqueBackend instance
311  */
312 UniqueBackend *
unique_backend_create(void)313 unique_backend_create (void)
314 {
315   const gchar *backend_name;
316   GType backend_gtype = G_TYPE_INVALID;
317 
318   backend_name = g_getenv ("UNIQUE_BACKEND");
319   if (!backend_name)
320     backend_name = UNIQUE_DEFAULT_BACKEND_S;
321 
322   if (backend_name && backend_name[0] != '\0')
323     {
324 #ifdef HAVE_BACON
325       if (strcmp (backend_name, "bacon") == 0)
326         backend_gtype = unique_backend_bacon_get_type ();
327 #endif
328 #ifdef HAVE_DBUS
329       if (strcmp (backend_name, "dbus") == 0)
330         backend_gtype = unique_backend_dbus_get_type ();
331 #endif /* HAVE_DBUS */
332 #if !defined(HAVE_BACON) && !defined(HAVE_DBUS)
333 #error Need either bacon or dbus
334 #endif
335     }
336 
337   return g_object_new (backend_gtype, NULL);
338 }
339