No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 #include "config.h"
4
5 #include <sys/types.h>
6 #include <sys/wait.h>
7
8 #include "shell-util.h"
9 #include <glib/gi18n-lib.h>
10 #include <gtk/gtk.h>
11 #include <gdk-pixbuf/gdk-pixbuf.h>
12
13 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
14 #include <langinfo.h>
15 #endif
16
17 #ifdef WITH_SYSTEMD
18 #include <systemd/sd-daemon.h>
19 #include <systemd/sd-login.h>
20 #endif
21
22 static void
23 stop_pick (ClutterActor *actor,
24 const ClutterColor *color)
25 {
26 g_signal_stop_emission_by_name (actor, "pick");
27 }
28
29 /**
30 * shell_util_set_hidden_from_pick:
31 * @actor: A #ClutterActor
32 * @hidden: Whether @actor should be hidden from pick
33 *
34 * If @hidden is %TRUE, hide @actor from pick even with a mode of
35 * %CLUTTER_PICK_ALL; if @hidden is %FALSE, unhide @actor.
36 */
37 void
38 shell_util_set_hidden_from_pick (ClutterActor *actor,
39 gboolean hidden)
40 {
41 gpointer existing_handler_data;
42
43 existing_handler_data = g_object_get_data (G_OBJECT (actor),
44 "shell-stop-pick");
45 if (hidden)
46 {
47 if (existing_handler_data != NULL)
48 return;
49 g_signal_connect (actor, "pick", G_CALLBACK (stop_pick), NULL);
50 g_object_set_data (G_OBJECT (actor),
51 "shell-stop-pick", GUINT_TO_POINTER (1));
52 }
53 else
54 {
55 if (existing_handler_data == NULL)
56 return;
57 g_signal_handlers_disconnect_by_func (actor, stop_pick, NULL);
58 g_object_set_data (G_OBJECT (actor), "shell-stop-pick", NULL);
59 }
60 }
61
62 /**
63 * shell_util_get_transformed_allocation:
64 * @actor: a #ClutterActor
65 * @box: (out): location to store returned box in stage coordinates
66 *
67 * This function is similar to a combination of clutter_actor_get_transformed_position(),
68 * and clutter_actor_get_transformed_size(), but unlike
69 * clutter_actor_get_transformed_size(), it always returns a transform
70 * of the current allocation, while clutter_actor_get_transformed_size() returns
71 * bad values (the transform of the requested size) if a relayout has been
72 * queued.
73 *
74 * This function is more convenient to use than
75 * clutter_actor_get_abs_allocation_vertices() if no transformation is in effect
76 * and also works around limitations in the GJS binding of arrays.
77 */
78 void
79 shell_util_get_transformed_allocation (ClutterActor *actor,
80 ClutterActorBox *box)
81 {
82 /* Code adapted from clutter-actor.c:
83 * Copyright 2006, 2007, 2008 OpenedHand Ltd
84 */
85 ClutterVertex v[4];
86 gfloat x_min, x_max, y_min, y_max;
87 gint i;
88
89 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
90
91 clutter_actor_get_abs_allocation_vertices (actor, v);
92
93 x_min = x_max = v[0].x;
94 y_min = y_max = v[0].y;
95
96 for (i = 1; i < G_N_ELEMENTS (v); ++i)
97 {
98 if (v[i].x < x_min)
99 x_min = v[i].x;
100
101 if (v[i].x > x_max)
102 x_max = v[i].x;
103
104 if (v[i].y < y_min)
105 y_min = v[i].y;
106
107 if (v[i].y > y_max)
108 y_max = v[i].y;
109 }
110
111 box->x1 = x_min;
112 box->y1 = y_min;
113 box->x2 = x_max;
114 box->y2 = y_max;
115 }
116
117 char *
118 shell_util_normalize_and_casefold (const char *str)
119 {
120 char *normalized, *result;
121
122 if (str == NULL)
123 return NULL;
124
125 normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
126 result = g_utf8_casefold (normalized, -1);
127 g_free (normalized);
128 return result;
129 }
130
131 /**
132 * shell_util_format_date:
133 * @format: a strftime-style string format, as parsed by
134 * g_date_time_format()
135 * @time_ms: milliseconds since 1970-01-01 00:00:00 UTC; the
136 * value returned by Date.getTime()
137 *
138 * Formats a date for the current locale. This should be
139 * used instead of the Spidermonkey Date.toLocaleFormat()
140 * extension because Date.toLocaleFormat() is buggy for
141 * Unicode format strings:
142 * https://bugzilla.mozilla.org/show_bug.cgi?id=508783
143 *
144 * Return value: the formatted date. If the date is
145 * outside of the range of a GDateTime (which contains
146 * any plausible dates we actually care about), will
147 * return an empty string.
148 */
149 char *
150 shell_util_format_date (const char *format,
151 gint64 time_ms)
152 {
153 GDateTime *datetime;
154 GTimeVal tv;
155 char *result;
156
157 tv.tv_sec = time_ms / 1000;
158 tv.tv_usec = (time_ms % 1000) * 1000;
159
160 datetime = g_date_time_new_from_timeval_local (&tv);
161 if (!datetime) /* time_ms is out of range of GDateTime */
162 return g_strdup ("");
163
164 result = g_date_time_format (datetime, format);
165
166 g_date_time_unref (datetime);
167 return result;
168 }
169
170 /**
171 * shell_util_get_week_start:
172 *
173 * Gets the first week day for the current locale, expressed as a
174 * number in the range 0..6, representing week days from Sunday to
175 * Saturday.
176 *
177 * Returns: A number representing the first week day for the current
178 * locale
179 */
180 /* Copied from gtkcalendar.c */
181 int
182 shell_util_get_week_start ()
183 {
184 int week_start;
185 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
186 union { unsigned int word; char *string; } langinfo;
187 int week_1stday = 0;
188 int first_weekday = 1;
189 guint week_origin;
190 #else
191 char *gtk_week_start;
192 #endif
193
194 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
195 langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
196 first_weekday = langinfo.string[0];
197 langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
198 week_origin = langinfo.word;
199 if (week_origin == 19971130) /* Sunday */
200 week_1stday = 0;
201 else if (week_origin == 19971201) /* Monday */
202 week_1stday = 1;
203 else
204 g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
205
206 week_start = (week_1stday + first_weekday - 1) % 7;
207 #else
208 /* Use a define to hide the string from xgettext */
209 # define GTK_WEEK_START "calendar:week_start:0"
210 gtk_week_start = dgettext ("gtk30", GTK_WEEK_START);
211
212 if (strncmp (gtk_week_start, "calendar:week_start:", 20) == 0)
213 week_start = *(gtk_week_start + 20) - '0';
214 else
215 week_start = -1;
216
217 if (week_start < 0 || week_start > 6)
218 {
219 g_warning ("Whoever translated calendar:week_start:0 for GTK+ "
220 "did so wrongly.\n");
221 week_start = 0;
222 }
223 #endif
224
225 return week_start;
226 }
227
228 /**
229 * shell_write_string_to_stream:
230 * @stream: a #GOutputStream
231 * @str: a UTF-8 string to write to @stream
232 * @error: location to store GError
233 *
234 * Write a string to a GOutputStream as UTF-8. This is a workaround
235 * for not having binary buffers in GJS.
236 *
237 * Return value: %TRUE if write succeeded
238 */
239 gboolean
240 shell_write_string_to_stream (GOutputStream *stream,
241 const char *str,
242 GError **error)
243 {
244 return g_output_stream_write_all (stream, str, strlen (str),
245 NULL, NULL, error);
246 }
247
248 /**
249 * shell_get_file_contents_utf8_sync:
250 * @path: UTF-8 encoded filename path
251 * @error: a #GError
252 *
253 * Synchronously load the contents of a file as a NUL terminated
254 * string, validating it as UTF-8. Embedded NUL characters count as
255 * invalid content.
256 *
257 * Returns: (transfer full): File contents
258 */
259 char *
260 shell_get_file_contents_utf8_sync (const char *path,
261 GError **error)
262 {
263 char *contents;
264 gsize len;
265 if (!g_file_get_contents (path, &contents, &len, error))
266 return NULL;
267 if (!g_utf8_validate (contents, len, NULL))
268 {
269 g_free (contents);
270 g_set_error (error,
271 G_IO_ERROR,
272 G_IO_ERROR_FAILED,
273 "File %s contains invalid UTF-8",
274 path);
275 return NULL;
276 }
277 return contents;
278 }
279
280 /**
281 * shell_session_is_active_for_systemd:
282 *
283 * Checks whether the session we are running in is currently active,
284 * i.e. in the foreground and ready for user input.
285 *
286 * Returns: TRUE if session is active
287 */
288 gboolean
289 shell_session_is_active_for_systemd (void)
290 {
291 /* If this isn't systemd, let's assume the session is active. */
292
293 #ifdef WITH_SYSTEMD
294 if (sd_booted () <= 0)
295 return TRUE;
296
297 return sd_session_is_active (NULL) != 0;
298 #else
299 return TRUE;
300 #endif
301 }
302
303 /**
304 * shell_util_wifexited:
305 * @status: the status returned by wait() or waitpid()
306 * @exit: (out): the actual exit status of the process
307 *
308 * Implements libc standard WIFEXITED, that cannot be used JS
309 * code.
310 * Returns: TRUE if the process exited normally, FALSE otherwise
311 */
312 gboolean
313 shell_util_wifexited (int status,
314 int *exit)
315 {
316 gboolean ret;
317
318 ret = WIFEXITED(status);
319
320 if (ret)
321 *exit = WEXITSTATUS(status);
322
323 return ret;
324 }
325
326 /**
327 * shell_util_create_pixbuf_from_data:
328 * @data: (array length=len) (element-type guint8) (transfer full):
329 * @len:
330 * @colorspace:
331 * @has_alpha:
332 * @bits_per_sample:
333 * @width:
334 * @height:
335 * @rowstride:
336 *
337 * Workaround for non-introspectability of gdk_pixbuf_from_data().
338 *
339 * Returns: (transfer full):
340 */
341 GdkPixbuf *
342 shell_util_create_pixbuf_from_data (const guchar *data,
343 gsize len,
344 GdkColorspace colorspace,
345 gboolean has_alpha,
346 int bits_per_sample,
347 int width,
348 int height,
349 int rowstride)
350 {
351 return gdk_pixbuf_new_from_data (data, colorspace, has_alpha,
352 bits_per_sample, width, height, rowstride,
353 (GdkPixbufDestroyNotify) g_free, NULL);
354 }