1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2010 Red Hat, Inc.
4    Copyright © 2006-2010 Collabora Ltd. <http://www.collabora.co.uk/>
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, see <http://www.gnu.org/licenses/>.
18 */
19 #include "config.h"
20 
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <glib-object.h>
26 #include "spice-util-priv.h"
27 #include "spice-util.h"
28 #include "spice-util-priv.h"
29 
30 /**
31  * SECTION:spice-util
32  * @short_description: version and debugging functions
33  * @title: Utilities
34  * @section_id:
35  * @stability: Stable
36  * @include: spice-client.h
37  *
38  * Various functions for debugging and informational purposes.
39  */
40 
41 static GOnce debug_once = G_ONCE_INIT;
42 
spice_util_enable_debug_messages(void)43 static void spice_util_enable_debug_messages(void)
44 {
45     const gchar *doms = g_getenv("G_MESSAGES_DEBUG");
46     if (!doms) {
47         g_setenv("G_MESSAGES_DEBUG", G_LOG_DOMAIN, 1);
48     } else if (g_str_equal(doms, "all")) {
49         return;
50     } else if (!strstr(doms, G_LOG_DOMAIN)) {
51         gchar *newdoms = g_strdup_printf("%s %s", doms, G_LOG_DOMAIN);
52         g_setenv("G_MESSAGES_DEBUG", newdoms, 1);
53         g_free(newdoms);
54     }
55 }
56 
57 /**
58  * spice_util_set_debug:
59  * @enabled: %TRUE or %FALSE
60  *
61  * Enable or disable Spice-GTK debugging messages.
62  **/
spice_util_set_debug(gboolean enabled)63 void spice_util_set_debug(gboolean enabled)
64 {
65     /* Make sure debug_once has been initialised
66      * with the value of SPICE_DEBUG already, otherwise
67      * spice_util_get_debug() may overwrite the value
68      * that was just set using spice_util_set_debug()
69      */
70     spice_util_get_debug();
71 
72     if (enabled) {
73         spice_util_enable_debug_messages();
74     }
75 
76     debug_once.retval = GINT_TO_POINTER(enabled);
77 }
78 
getenv_debug(gpointer data)79 static gpointer getenv_debug(gpointer data)
80 {
81     gboolean debug;
82 
83     debug = (g_getenv("SPICE_DEBUG") != NULL);
84     if (debug)
85         spice_util_enable_debug_messages();
86 
87     return GINT_TO_POINTER(debug);
88 }
89 
spice_util_get_debug(void)90 gboolean spice_util_get_debug(void)
91 {
92     g_once(&debug_once, getenv_debug, NULL);
93 
94     return GPOINTER_TO_INT(debug_once.retval);
95 }
96 
97 /**
98  * spice_util_get_version_string:
99  *
100  * Gets the version string
101  *
102  * Returns: Spice-GTK version as a const string.
103  **/
spice_util_get_version_string(void)104 const gchar *spice_util_get_version_string(void)
105 {
106     return VERSION;
107 }
108 
109 G_GNUC_INTERNAL
spice_strv_contains(const GStrv strv,const gchar * str)110 gboolean spice_strv_contains(const GStrv strv, const gchar *str)
111 {
112     int i;
113 
114     if (strv == NULL)
115         return FALSE;
116 
117     for (i = 0; strv[i] != NULL; i++)
118         if (g_str_equal(strv[i], str))
119             return TRUE;
120 
121     return FALSE;
122 }
123 
124 /**
125  * spice_uuid_to_string:
126  * @uuid: UUID byte array
127  *
128  * Creates a string representation of @uuid, of the form
129  * "06e023d5-86d8-420e-8103-383e4566087a"
130  *
131  * Returns: A string that should be freed with g_free().
132  * Since: 0.22
133  **/
spice_uuid_to_string(const guint8 uuid[16])134 gchar* spice_uuid_to_string(const guint8 uuid[16])
135 {
136     return g_strdup_printf(UUID_FMT, uuid[0], uuid[1],
137                            uuid[2], uuid[3], uuid[4], uuid[5],
138                            uuid[6], uuid[7], uuid[8], uuid[9],
139                            uuid[10], uuid[11], uuid[12], uuid[13],
140                            uuid[14], uuid[15]);
141 }
142 
143 typedef struct {
144     GObject *instance;
145     GObject *observer;
146     GClosure *closure;
147     gulong handler_id;
148 } WeakHandlerCtx;
149 
150 static WeakHandlerCtx *
whc_new(GObject * instance,GObject * observer)151 whc_new (GObject *instance,
152          GObject *observer)
153 {
154     WeakHandlerCtx *ctx = g_new0 (WeakHandlerCtx, 1);
155 
156     ctx->instance = instance;
157     ctx->observer = observer;
158 
159     return ctx;
160 }
161 
162 static void
whc_free(WeakHandlerCtx * ctx)163 whc_free (WeakHandlerCtx *ctx)
164 {
165     g_free (ctx);
166 }
167 
168 static void observer_destroyed_cb (gpointer, GObject *);
169 static void closure_invalidated_cb (gpointer, GClosure *);
170 
171 /*
172  * If signal handlers are removed before the object is destroyed, this
173  * callback will never get triggered.
174  */
175 static void
instance_destroyed_cb(gpointer ctx_,GObject * where_the_instance_was)176 instance_destroyed_cb (gpointer ctx_,
177                        GObject *where_the_instance_was)
178 {
179     WeakHandlerCtx *ctx = ctx_;
180 
181     /* No need to disconnect the signal here, the instance has gone away. */
182     g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
183     g_closure_remove_invalidate_notifier (ctx->closure, ctx,
184                                           closure_invalidated_cb);
185     whc_free (ctx);
186 }
187 
188 /* Triggered when the observer is destroyed. */
189 static void
observer_destroyed_cb(gpointer ctx_,GObject * where_the_observer_was)190 observer_destroyed_cb (gpointer ctx_,
191                        GObject *where_the_observer_was)
192 {
193     WeakHandlerCtx *ctx = ctx_;
194 
195     g_closure_remove_invalidate_notifier (ctx->closure, ctx,
196                                           closure_invalidated_cb);
197     g_signal_handler_disconnect (ctx->instance, ctx->handler_id);
198     g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
199     whc_free (ctx);
200 }
201 
202 /* Triggered when either object is destroyed or the handler is disconnected. */
203 static void
closure_invalidated_cb(gpointer ctx_,GClosure * where_the_closure_was)204 closure_invalidated_cb (gpointer ctx_,
205                         GClosure *where_the_closure_was)
206 {
207     WeakHandlerCtx *ctx = ctx_;
208 
209     g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
210     g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
211     whc_free (ctx);
212 }
213 
214 /* Copied from tp_g_signal_connect_object. See documentation. */
215 /**
216   * spice_g_signal_connect_object: (skip)
217   * @instance: the instance to connect to.
218   * @detailed_signal: a string of the form "signal-name::detail".
219   * @c_handler: the #GCallback to connect.
220   * @gobject: the object to pass as data to @c_handler.
221   * @connect_flags: a combination of #GConnectFlags.
222   *
223   * Similar to g_signal_connect_object() but will delete connection
224   * when any of the objects is destroyed.
225   *
226   * Returns: the handler id.
227   */
spice_g_signal_connect_object(gpointer instance,const gchar * detailed_signal,GCallback c_handler,gpointer gobject,GConnectFlags connect_flags)228 gulong spice_g_signal_connect_object (gpointer instance,
229                                       const gchar *detailed_signal,
230                                       GCallback c_handler,
231                                       gpointer gobject,
232                                       GConnectFlags connect_flags)
233 {
234     GObject *instance_obj = G_OBJECT (instance);
235     WeakHandlerCtx *ctx = whc_new (instance_obj, gobject);
236 
237     g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
238     g_return_val_if_fail (detailed_signal != NULL, 0);
239     g_return_val_if_fail (c_handler != NULL, 0);
240     g_return_val_if_fail (G_IS_OBJECT (gobject), 0);
241     g_return_val_if_fail (
242                           (connect_flags & ~(G_CONNECT_AFTER|G_CONNECT_SWAPPED)) == 0, 0);
243 
244     if (connect_flags & G_CONNECT_SWAPPED)
245         ctx->closure = g_cclosure_new_object_swap (c_handler, gobject);
246     else
247         ctx->closure = g_cclosure_new_object (c_handler, gobject);
248 
249     ctx->handler_id = g_signal_connect_closure (instance, detailed_signal,
250                                                 ctx->closure, (connect_flags & G_CONNECT_AFTER) ? TRUE : FALSE);
251 
252     g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx);
253     g_object_weak_ref (gobject, observer_destroyed_cb, ctx);
254     g_closure_add_invalidate_notifier (ctx->closure, ctx,
255                                        closure_invalidated_cb);
256 
257     return ctx->handler_id;
258 }
259 
260 G_GNUC_INTERNAL
spice_yes_no(gboolean value)261 const gchar* spice_yes_no(gboolean value)
262 {
263     return value ? "yes" : "no";
264 }
265 
266 G_GNUC_INTERNAL
spice_make_scancode(guint scancode,gboolean release)267 guint16 spice_make_scancode(guint scancode, gboolean release)
268 {
269     SPICE_DEBUG("%s: %s scancode %u",
270                 __FUNCTION__, release ? "release" : "", scancode);
271 
272     scancode &= 0x37f;
273     if (release)
274         scancode |= 0x80;
275     if (scancode < 0x100)
276         return scancode;
277     return GUINT16_SWAP_LE_BE(0xe000 | (scancode - 0x100));
278 }
279 
280 typedef enum {
281     NEWLINE_TYPE_LF,
282     NEWLINE_TYPE_CR_LF
283 } NewlineType;
284 
get_line(const gchar * str,gsize len,NewlineType type,gsize * nl_len)285 static gssize get_line(const gchar *str, gsize len,
286                        NewlineType type, gsize *nl_len)
287 {
288     const gchar *p, *endl;
289     gsize nl = 0;
290 
291     endl = (type == NEWLINE_TYPE_CR_LF) ? "\r\n" : "\n";
292     p = g_strstr_len(str, len, endl);
293     if (p) {
294         len = p - str;
295         nl = strlen(endl);
296     }
297 
298     *nl_len = nl;
299     return len;
300 }
301 
302 
spice_convert_newlines(const gchar * str,gssize len,NewlineType from,NewlineType to)303 static gchar* spice_convert_newlines(const gchar *str, gssize len,
304                                      NewlineType from,
305                                      NewlineType to)
306 {
307     gssize length;
308     gsize nl;
309     GString *output;
310     gint i;
311 
312     g_return_val_if_fail(str != NULL, NULL);
313     g_return_val_if_fail(len >= -1, NULL);
314     /* only 2 supported combinations */
315     g_return_val_if_fail((from == NEWLINE_TYPE_LF &&
316                           to == NEWLINE_TYPE_CR_LF) ||
317                          (from == NEWLINE_TYPE_CR_LF &&
318                           to == NEWLINE_TYPE_LF), NULL);
319 
320     if (len == -1)
321         len = strlen(str);
322     /* sometime we get \0 terminated strings, skip that, or it fails
323        to utf8 validate line with \0 end */
324     else if (len > 0 && str[len-1] == 0)
325         len -= 1;
326 
327     /* allocate worst case, if it's small enough, we don't care much,
328      * if it's big, malloc will put us in mmap'd region, and we can
329      * over allocate.
330      */
331     output = g_string_sized_new(len * 2 + 1);
332 
333     for (i = 0; i < len; i += length + nl) {
334         length = get_line(str + i, len - i, from, &nl);
335         if (length < 0)
336             break;
337 
338         g_string_append_len(output, str + i, length);
339 
340         if (nl) {
341             /* let's not double \r if it's already in the line */
342             if (to == NEWLINE_TYPE_CR_LF &&
343                 (output->len == 0 || output->str[output->len - 1] != '\r'))
344                 g_string_append_c(output, '\r');
345 
346             g_string_append_c(output, '\n');
347         }
348     }
349 
350     return g_string_free(output, FALSE);
351 }
352 
353 G_GNUC_INTERNAL
spice_dos2unix(const gchar * str,gssize len)354 gchar* spice_dos2unix(const gchar *str, gssize len)
355 {
356     return spice_convert_newlines(str, len,
357                                   NEWLINE_TYPE_CR_LF,
358                                   NEWLINE_TYPE_LF);
359 }
360 
361 G_GNUC_INTERNAL
spice_unix2dos(const gchar * str,gssize len)362 gchar* spice_unix2dos(const gchar *str, gssize len)
363 {
364     return spice_convert_newlines(str, len,
365                                   NEWLINE_TYPE_LF,
366                                   NEWLINE_TYPE_CR_LF);
367 }
368 
buf_is_ones(unsigned size,const guint8 * data)369 static bool buf_is_ones(unsigned size, const guint8 *data)
370 {
371     int i;
372 
373     for (i = 0 ; i < size; ++i) {
374         if (data[i] != 0xff) {
375             return false;
376         }
377     }
378     return true;
379 }
380 
is_edge_helper(const guint8 * xor,int bpl,int x,int y)381 static bool is_edge_helper(const guint8 *xor, int bpl, int x, int y)
382 {
383     return (xor[bpl * y + (x / 8)] & (0x80 >> (x % 8))) > 0;
384 }
385 
is_edge(unsigned width,unsigned height,const guint8 * xor,int bpl,int x,int y)386 static bool is_edge(unsigned width, unsigned height, const guint8 *xor, int bpl, int x, int y)
387 {
388     if (x == 0 || x == width -1 || y == 0 || y == height - 1) {
389         return 0;
390     }
391 #define P(x, y) is_edge_helper(xor, bpl, x, y)
392     return !P(x, y) && (P(x - 1, y + 1) || P(x, y + 1) || P(x + 1, y + 1) ||
393                         P(x - 1, y)     ||                P(x + 1, y)     ||
394                         P(x - 1, y - 1) || P(x, y - 1) || P(x + 1, y - 1));
395 #undef P
396 }
397 
398 /* Mono cursors have two places, "and" and "xor". If a bit is 1 in both, it
399  * means invertion of the corresponding pixel in the display. Since X11 (and
400  * gdk) doesn't do invertion, instead we do edge detection and turn the
401  * sorrounding edge pixels black, and the invert-me pixels white. To
402  * illustrate:
403  *
404  *  and   xor      dest RGB (1=0xffffff, 0=0x000000)
405  *
406  *                        dest alpha (1=0xff, 0=0x00)
407  *
408  * 11111 00000     00000  00000
409  * 11111 00000     00000  01110
410  * 11111 00100 =>  00100  01110
411  * 11111 00100     00100  01110
412  * 11111 00000     00000  01110
413  * 11111 00000     00000  00000
414  *
415  * See tests/util.c for more tests
416  *
417  * Notes:
418  *  Assumes width >= 8 (i.e. bytes per line is at least 1)
419  *  Assumes edges are not on the boundary (first/last line/column) for simplicity
420  *
421  */
422 G_GNUC_INTERNAL
spice_mono_edge_highlight(unsigned width,unsigned height,const guint8 * and,const guint8 * xor,guint8 * dest)423 void spice_mono_edge_highlight(unsigned width, unsigned height,
424                                const guint8 *and, const guint8 *xor, guint8 *dest)
425 {
426     int bpl = (width + 7) / 8;
427     bool and_ones = buf_is_ones(height * bpl, and);
428     int x, y, bit;
429     const guint8 *xor_base = xor;
430 
431     for (y = 0; y < height; y++) {
432         bit = 0x80;
433         for (x = 0; x < width; x++, dest += 4) {
434             if (is_edge(width, height, xor_base, bpl, x, y) && and_ones) {
435                 dest[0] = 0x00;
436                 dest[1] = 0x00;
437                 dest[2] = 0x00;
438                 dest[3] = 0xff;
439                 goto next_bit;
440             }
441             if (and[x/8] & bit) {
442                 if (xor[x/8] & bit) {
443                     dest[0] = 0xff;
444                     dest[1] = 0xff;
445                     dest[2] = 0xff;
446                     dest[3] = 0xff;
447                 } else {
448                     /* unchanged -> transparent */
449                     dest[0] = 0x00;
450                     dest[1] = 0x00;
451                     dest[2] = 0x00;
452                     dest[3] = 0x00;
453                 }
454             } else {
455                 if (xor[x/8] & bit) {
456                     /* set -> white */
457                     dest[0] = 0xff;
458                     dest[1] = 0xff;
459                     dest[2] = 0xff;
460                     dest[3] = 0xff;
461                 } else {
462                     /* clear -> black */
463                     dest[0] = 0x00;
464                     dest[1] = 0x00;
465                     dest[2] = 0x00;
466                     dest[3] = 0xff;
467                 }
468             }
469         next_bit:
470             bit >>= 1;
471             if (bit == 0) {
472                 bit = 0x80;
473             }
474         }
475         and += bpl;
476         xor += bpl;
477     }
478 }
479