gnome-shell/src/shell-util.c
Giovanni Campagna 2b8414a453 Replace ShellXFixesCursor with MetaCursorTracker
Mutter now includes an object with the same purpose and functionality
as ShellXFixesCursor, so we can replace our XFixes code with it
and work under wayland too.

https://bugzilla.gnome.org/show_bug.cgi?id=705911
2013-08-19 16:14:12 +02:00

444 lines
12 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include <sys/types.h>
#include <sys/wait.h>
#include "shell-util.h"
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk/gdkx.h>
#include <X11/extensions/XTest.h>
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
#include <langinfo.h>
#endif
static void
stop_pick (ClutterActor *actor,
const ClutterColor *color)
{
g_signal_stop_emission_by_name (actor, "pick");
}
/**
* shell_util_set_hidden_from_pick:
* @actor: A #ClutterActor
* @hidden: Whether @actor should be hidden from pick
*
* If @hidden is %TRUE, hide @actor from pick even with a mode of
* %CLUTTER_PICK_ALL; if @hidden is %FALSE, unhide @actor.
*/
void
shell_util_set_hidden_from_pick (ClutterActor *actor,
gboolean hidden)
{
gpointer existing_handler_data;
existing_handler_data = g_object_get_data (G_OBJECT (actor),
"shell-stop-pick");
if (hidden)
{
if (existing_handler_data != NULL)
return;
g_signal_connect (actor, "pick", G_CALLBACK (stop_pick), NULL);
g_object_set_data (G_OBJECT (actor),
"shell-stop-pick", GUINT_TO_POINTER (1));
}
else
{
if (existing_handler_data == NULL)
return;
g_signal_handlers_disconnect_by_func (actor, stop_pick, NULL);
g_object_set_data (G_OBJECT (actor), "shell-stop-pick", NULL);
}
}
/**
* shell_util_get_transformed_allocation:
* @actor: a #ClutterActor
* @box: (out): location to store returned box in stage coordinates
*
* This function is similar to a combination of clutter_actor_get_transformed_position(),
* and clutter_actor_get_transformed_size(), but unlike
* clutter_actor_get_transformed_size(), it always returns a transform
* of the current allocation, while clutter_actor_get_transformed_size() returns
* bad values (the transform of the requested size) if a relayout has been
* queued.
*
* This function is more convenient to use than
* clutter_actor_get_abs_allocation_vertices() if no transformation is in effect
* and also works around limitations in the GJS binding of arrays.
*/
void
shell_util_get_transformed_allocation (ClutterActor *actor,
ClutterActorBox *box)
{
/* Code adapted from clutter-actor.c:
* Copyright 2006, 2007, 2008 OpenedHand Ltd
*/
ClutterVertex v[4];
gfloat x_min, x_max, y_min, y_max;
gint i;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
clutter_actor_get_abs_allocation_vertices (actor, v);
x_min = x_max = v[0].x;
y_min = y_max = v[0].y;
for (i = 1; i < G_N_ELEMENTS (v); ++i)
{
if (v[i].x < x_min)
x_min = v[i].x;
if (v[i].x > x_max)
x_max = v[i].x;
if (v[i].y < y_min)
y_min = v[i].y;
if (v[i].y > y_max)
y_max = v[i].y;
}
box->x1 = x_min;
box->y1 = y_min;
box->x2 = x_max;
box->y2 = y_max;
}
char *
shell_util_normalize_and_casefold (const char *str)
{
char *normalized, *result;
if (str == NULL)
return NULL;
/* NOTE: 'ALL' is equivalent to 'NFKD'. If this is ever updated, please
* update the unaccenting mechanism as well. */
normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
result = g_utf8_casefold (normalized, -1);
g_free (normalized);
return result;
}
/* Combining diacritical mark?
* Basic range: [0x0300,0x036F]
* Supplement: [0x1DC0,0x1DFF]
* For Symbols: [0x20D0,0x20FF]
* Half marks: [0xFE20,0xFE2F]
*/
#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \
((c) >= 0x1DC0 && (c) <= 0x1DFF) || \
((c) >= 0x20D0 && (c) <= 0x20FF) || \
((c) >= 0xFE20 && (c) <= 0xFE2F))
/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL
* Originally written by Aleksander Morgado <aleksander@gnu.org>
*/
char *
shell_util_normalize_casefold_and_unaccent (const char *str)
{
char *tmp;
gsize i = 0, j = 0, ilen;
if (str == NULL)
return NULL;
/* Get the NFKD-normalized and casefolded string */
tmp = shell_util_normalize_and_casefold (str);
ilen = strlen (tmp);
while (i < ilen)
{
gunichar unichar;
gchar *next_utf8;
gint utf8_len;
/* Get next character of the word as UCS4 */
unichar = g_utf8_get_char_validated (&tmp[i], -1);
/* Invalid UTF-8 character or end of original string. */
if (unichar == (gunichar) -1 ||
unichar == (gunichar) -2)
{
break;
}
/* Find next UTF-8 character */
next_utf8 = g_utf8_next_char (&tmp[i]);
utf8_len = next_utf8 - &tmp[i];
if (IS_CDM_UCS4 ((guint32) unichar))
{
/* If the given unichar is a combining diacritical mark,
* just update the original index, not the output one */
i += utf8_len;
continue;
}
/* If already found a previous combining
* diacritical mark, indexes are different so
* need to copy characters. As output and input
* buffers may overlap, need to use memmove
* instead of memcpy */
if (i != j)
{
memmove (&tmp[j], &tmp[i], utf8_len);
}
/* Update both indexes */
i += utf8_len;
j += utf8_len;
}
/* Force proper string end */
tmp[j] = '\0';
return tmp;
}
/**
* shell_util_format_date:
* @format: a strftime-style string format, as parsed by
* g_date_time_format()
* @time_ms: milliseconds since 1970-01-01 00:00:00 UTC; the
* value returned by Date.getTime()
*
* Formats a date for the current locale. This should be
* used instead of the Spidermonkey Date.toLocaleFormat()
* extension because Date.toLocaleFormat() is buggy for
* Unicode format strings:
* https://bugzilla.mozilla.org/show_bug.cgi?id=508783
*
* Return value: the formatted date. If the date is
* outside of the range of a GDateTime (which contains
* any plausible dates we actually care about), will
* return an empty string.
*/
char *
shell_util_format_date (const char *format,
gint64 time_ms)
{
GDateTime *datetime;
GTimeVal tv;
char *result;
tv.tv_sec = time_ms / 1000;
tv.tv_usec = (time_ms % 1000) * 1000;
datetime = g_date_time_new_from_timeval_local (&tv);
if (!datetime) /* time_ms is out of range of GDateTime */
return g_strdup ("");
result = g_date_time_format (datetime, format);
g_date_time_unref (datetime);
return result;
}
/**
* shell_util_get_week_start:
*
* Gets the first week day for the current locale, expressed as a
* number in the range 0..6, representing week days from Sunday to
* Saturday.
*
* Returns: A number representing the first week day for the current
* locale
*/
/* Copied from gtkcalendar.c */
int
shell_util_get_week_start ()
{
int week_start;
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
union { unsigned int word; char *string; } langinfo;
int week_1stday = 0;
int first_weekday = 1;
guint week_origin;
#else
char *gtk_week_start;
#endif
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
first_weekday = langinfo.string[0];
langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
week_origin = langinfo.word;
if (week_origin == 19971130) /* Sunday */
week_1stday = 0;
else if (week_origin == 19971201) /* Monday */
week_1stday = 1;
else
g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
week_start = (week_1stday + first_weekday - 1) % 7;
#else
/* Use a define to hide the string from xgettext */
# define GTK_WEEK_START "calendar:week_start:0"
gtk_week_start = dgettext ("gtk30", GTK_WEEK_START);
if (strncmp (gtk_week_start, "calendar:week_start:", 20) == 0)
week_start = *(gtk_week_start + 20) - '0';
else
week_start = -1;
if (week_start < 0 || week_start > 6)
{
g_warning ("Whoever translated calendar:week_start:0 for GTK+ "
"did so wrongly.\n");
week_start = 0;
}
#endif
return week_start;
}
/**
* shell_write_string_to_stream:
* @stream: a #GOutputStream
* @str: a UTF-8 string to write to @stream
* @error: location to store GError
*
* Write a string to a GOutputStream as UTF-8. This is a workaround
* for not having binary buffers in GJS.
*
* Return value: %TRUE if write succeeded
*/
gboolean
shell_write_string_to_stream (GOutputStream *stream,
const char *str,
GError **error)
{
return g_output_stream_write_all (stream, str, strlen (str),
NULL, NULL, error);
}
/**
* shell_get_file_contents_utf8_sync:
* @path: UTF-8 encoded filename path
* @error: a #GError
*
* Synchronously load the contents of a file as a NUL terminated
* string, validating it as UTF-8. Embedded NUL characters count as
* invalid content.
*
* Returns: (transfer full): File contents
*/
char *
shell_get_file_contents_utf8_sync (const char *path,
GError **error)
{
char *contents;
gsize len;
if (!g_file_get_contents (path, &contents, &len, error))
return NULL;
if (!g_utf8_validate (contents, len, NULL))
{
g_free (contents);
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"File %s contains invalid UTF-8",
path);
return NULL;
}
return contents;
}
/**
* shell_util_wifexited:
* @status: the status returned by wait() or waitpid()
* @exit: (out): the actual exit status of the process
*
* Implements libc standard WIFEXITED, that cannot be used JS
* code.
* Returns: TRUE if the process exited normally, FALSE otherwise
*/
gboolean
shell_util_wifexited (int status,
int *exit)
{
gboolean ret;
ret = WIFEXITED(status);
if (ret)
*exit = WEXITSTATUS(status);
return ret;
}
/**
* shell_util_create_pixbuf_from_data:
* @data: (array length=len) (element-type guint8) (transfer full):
* @len:
* @colorspace:
* @has_alpha:
* @bits_per_sample:
* @width:
* @height:
* @rowstride:
*
* Workaround for non-introspectability of gdk_pixbuf_from_data().
*
* Returns: (transfer full):
*/
GdkPixbuf *
shell_util_create_pixbuf_from_data (const guchar *data,
gsize len,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride)
{
return gdk_pixbuf_new_from_data (data, colorspace, has_alpha,
bits_per_sample, width, height, rowstride,
(GdkPixbufDestroyNotify) g_free, NULL);
}
/**
* shell_util_wake_up_screen:
*
* Send a fake key event, resetting the IDLETIME counter and
* causing gnome-settings-daemon to wake up the screen.
*/
/* Shamelessly taken from gnome-settings-daemon/plugins/power/gpm-common.c */
void
shell_util_wake_up_screen (void)
{
static gboolean inited = FALSE;
static KeyCode keycode1, keycode2;
static gboolean first_keycode = FALSE;
if (inited == FALSE) {
keycode1 = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_KEY_Alt_L);
keycode2 = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_KEY_Alt_R);
}
gdk_error_trap_push ();
/* send a left or right alt key; first press, then release */
XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), first_keycode ? keycode1 : keycode2, True, CurrentTime);
XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), first_keycode ? keycode1 : keycode2, False, CurrentTime);
first_keycode = !first_keycode;
gdk_error_trap_pop_ignored ();
}
void
shell_util_cursor_tracker_to_clutter (MetaCursorTracker *tracker,
ClutterTexture *texture)
{
CoglTexture *sprite;
sprite = meta_cursor_tracker_get_sprite (tracker);
clutter_texture_set_cogl_texture (texture, sprite);
}