~ubuntu-branches/ubuntu/wily/lxpanel/wily-proposed

« back to all changes in this revision

Viewing changes to plugins/xkb/xkb.c

  • Committer: Package Import Robot
  • Author(s): Julien Lavergne
  • Date: 2015-01-31 15:30:45 UTC
  • mfrom: (1.1.19) (44.1.1 vivid-proposed)
  • Revision ID: package-import@ubuntu.com-20150131153045-iabx5uuxwf2p9sl3
Tags: 0.7.2-1ubuntu1
* Merge with Debian. Ubuntu remaining changes:
* debian/control:
 - Add libindicator-dev build-depends.
 - Add a recommend on xterm | pavucontrol | gnome-alsamixer to enable the
   mixer on the sound applet. (LP: #957749).
 - Add indicator plugin binary.
 - Add build-depends on libicu-dev for weather plugin.
* debian/local/source_lxpanel.py:
 - Add apport hook.
* debian/lxpanel.install:
 - Install all files except indicators.
* debian/lxpanel-indicator-applet-plugin.install:
 - Install indicator plugin.
* debian/rules:
 - Add --enable-indicator-support flag.
 - Add dh_install --fail-missing.
* debian/patches:
 - 04_disable_gtk3_indicators.patch: Hide incompatible indicators in the
   preference menu (LP: #1165245).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright (c) 2010 LxDE Developers, see the file AUTHORS for details.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software Foundation,
 
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
17
 */
 
18
 
 
19
/* Originally derived from xfce4-xkb-plugin, Copyright 2004 Alexander Iliev,
 
20
 * which credits Michael Glickman. */
 
21
 
 
22
/* Modified by Giuseppe Penone <giuspen@gmail.com> starting from 2012-07 and lxpanel 0.5.10 */
 
23
 
 
24
#include "xkb.h"
 
25
 
 
26
#include <stdio.h>
 
27
#include <stdlib.h>
 
28
#include <string.h>
 
29
 
 
30
#include <X11/XKBlib.h>
 
31
 
 
32
#include <gtk/gtk.h>
 
33
#include <gdk-pixbuf/gdk-pixbuf.h>
 
34
#include <glib.h>
 
35
 
 
36
/* The X Keyboard Extension: Library Specification
 
37
 * http://www.xfree86.org/current/XKBlib.pdf */
 
38
 
 
39
typedef enum
 
40
{
 
41
    NEW_KBD_STATE_NOTIFY_IGNORE_NO,
 
42
    NEW_KBD_STATE_NOTIFY_IGNORE_YES_SET,
 
43
    NEW_KBD_STATE_NOTIFY_IGNORE_YES_ALL,
 
44
 
 
45
} t_new_kbd_notify_ignore;
 
46
 
 
47
static void             xkb_enter_locale_by_process(XkbPlugin * xkb);
 
48
static void             refresh_group_xkb(XkbPlugin * xkb);
 
49
static int              initialize_keyboard_description(XkbPlugin * xkb);
 
50
static GdkFilterReturn  xkb_event_filter(GdkXEvent * xevent, GdkEvent * event, XkbPlugin * xkb);
 
51
 
 
52
static t_new_kbd_notify_ignore  xkb_new_kbd_notify_ignore = NEW_KBD_STATE_NOTIFY_IGNORE_NO;
 
53
 
 
54
 
 
55
static gboolean xkb_new_kbd_notify_ignore_slot(gpointer p_data)
 
56
{
 
57
    xkb_new_kbd_notify_ignore = NEW_KBD_STATE_NOTIFY_IGNORE_NO;
 
58
    return FALSE; // remove source
 
59
}
 
60
 
 
61
/* Insert a process and its layout into the hash table. */
 
62
static void xkb_enter_locale_by_process(XkbPlugin * xkb)
 
63
{
 
64
    if ((xkb->p_hash_table_group != NULL) && (fb_ev_active_window(fbev) != None))
 
65
    {
 
66
        Window * win = fb_ev_active_window(fbev);
 
67
        if (*win != None)
 
68
            g_hash_table_insert(xkb->p_hash_table_group, GINT_TO_POINTER(*win), GINT_TO_POINTER(xkb->current_group_xkb_no));
 
69
    }
 
70
}
 
71
 
 
72
/* Return the current group Xkb ID. */
 
73
int xkb_get_current_group_xkb_no(XkbPlugin * xkb)
 
74
{
 
75
    return xkb->current_group_xkb_no;
 
76
}
 
77
 
 
78
/* Return the count of members in the current group. */
 
79
int xkb_get_group_count(XkbPlugin * xkb)
 
80
{
 
81
  return xkb->group_count;
 
82
}
 
83
 
 
84
/* Get the current group name. */
 
85
const char * xkb_get_current_group_name(XkbPlugin * xkb)
 
86
{
 
87
    return xkb->group_names[xkb->current_group_xkb_no];
 
88
}
 
89
 
 
90
/* Convert a group number to a symbol name. */
 
91
const char * xkb_get_symbol_name_by_res_no(XkbPlugin * xkb, int n)
 
92
{
 
93
    return xkb->symbol_names[n];
 
94
}
 
95
 
 
96
/* Get the current symbol name. */
 
97
const char * xkb_get_current_symbol_name(XkbPlugin * xkb)
 
98
{
 
99
    return xkb_get_symbol_name_by_res_no(xkb, xkb->current_group_xkb_no);
 
100
}
 
101
 
 
102
/* Get the current symbol name converted to lowercase. */
 
103
const char * xkb_get_current_symbol_name_lowercase(XkbPlugin * xkb)
 
104
{
 
105
    const char * tmp = xkb_get_current_symbol_name(xkb);
 
106
    return ((tmp != NULL) ? g_utf8_strdown(tmp, -1) : NULL);
 
107
}
 
108
 
 
109
/* Refresh current group number from Xkb state. */
 
110
static void refresh_group_xkb(XkbPlugin * xkb)
 
111
{
 
112
    /* Get the current group number.
 
113
     * This shouldn't be necessary, but mask the group number down for safety. */
 
114
    XkbStateRec xkb_state;
 
115
    XkbGetState(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), XkbUseCoreKbd, &xkb_state);
 
116
    xkb->current_group_xkb_no = xkb_state.group & (XkbNumKbdGroups - 1);
 
117
}
 
118
 
 
119
/* Initialize the keyboard description initially or after a NewKeyboard event. */
 
120
static int initialize_keyboard_description(XkbPlugin * xkb)
 
121
{
 
122
    /* Allocate a keyboard description. */
 
123
    XkbDescRec * xkb_desc = XkbAllocKeyboard();
 
124
    if (xkb_desc == NULL)
 
125
        g_warning("XkbAllocKeyboard failed\n");
 
126
    else
 
127
    {
 
128
        /* Read necessary values into the keyboard description. */
 
129
        Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
 
130
        XkbGetControls(xdisplay, XkbAllControlsMask, xkb_desc);
 
131
        XkbGetNames(xdisplay, XkbSymbolsNameMask | XkbGroupNamesMask, xkb_desc);
 
132
        if ((xkb_desc->names == NULL) || (xkb_desc->ctrls == NULL) || (xkb_desc->names->groups == NULL))
 
133
            g_warning("XkbGetControls/XkbGetNames failed\n");
 
134
        else
 
135
        {
 
136
            /* Get the group name of each keyboard layout.  Infer the group count from the highest available. */
 
137
            Atom * group_source = xkb_desc->names->groups;
 
138
            int i;
 
139
            for (i = 0; i < XkbNumKbdGroups; i += 1)
 
140
            {
 
141
                g_free(xkb->group_names[i]);
 
142
                xkb->group_names[i] = NULL;
 
143
                if (group_source[i] != None)
 
144
                {
 
145
                    xkb->group_count = i + 1;
 
146
                    char * p = XGetAtomName(xdisplay, group_source[i]);
 
147
                    xkb->group_names[i] = g_strdup(p);
 
148
                    XFree(p);
 
149
                }
 
150
            }
 
151
 
 
152
            /* Reinitialize the symbol name storage. */
 
153
            for (i = 0; i < XkbNumKbdGroups; i += 1)
 
154
            {
 
155
                g_free(xkb->symbol_names[i]);
 
156
                xkb->symbol_names[i] = NULL;
 
157
            }
 
158
 
 
159
            /* Get the symbol name of all keyboard layouts.
 
160
             * This is a plus-sign separated string. */
 
161
            if (xkb_desc->names->symbols != None)
 
162
            {
 
163
                char * symbol_string = XGetAtomName(xdisplay, xkb_desc->names->symbols);
 
164
                if (symbol_string != NULL)
 
165
                {
 
166
                    char * p = symbol_string;
 
167
                    char * q = p;
 
168
                    int symbol_group_number = 0;
 
169
                    for ( ; symbol_group_number < XkbNumKbdGroups; p += 1)
 
170
                    {
 
171
                        char c = *p;
 
172
                        if ((c == '\0') || (c == '+'))
 
173
                        {
 
174
                            /* End of a symbol.  Ignore the symbols "pc" and "inet" and "group". */
 
175
                            *p = '\0';
 
176
                            if ((strcmp(q, "pc") != 0) && (strcmp(q, "inet") != 0) && (strcmp(q, "group") != 0))
 
177
                            {
 
178
                                xkb->symbol_names[symbol_group_number] = g_ascii_strup(q, -1);
 
179
                                symbol_group_number += 1;
 
180
                            }
 
181
                            if (c == '\0')
 
182
                                break;
 
183
                            q = p + 1;
 
184
                        }
 
185
                        else if ((c == ':') && (p[1] >= '1') && (p[1] < ('1' + XkbNumKbdGroups)))
 
186
                        {
 
187
                            /* Construction ":n" at the end of a symbol.  The digit is a one-based index of the symbol.
 
188
                             * If not present, we will default to "next index". */
 
189
                            *p = '\0';
 
190
                            symbol_group_number = p[1] - '1';
 
191
                            xkb->symbol_names[symbol_group_number] = g_ascii_strup(q, -1);
 
192
                            symbol_group_number += 1;
 
193
                            p += 2;
 
194
                            if (*p == '\0')
 
195
                                break;
 
196
                            q = p + 1;
 
197
                        }
 
198
                        else if ((*p >= 'A') && (*p <= 'Z'))
 
199
                            *p |= 'a' - 'A';
 
200
                        else if ((*p < 'a') || (*p > 'z'))
 
201
                            *p = '\0';
 
202
                    }
 
203
 
 
204
                    /* Crosscheck the group count determined from the "ctrls" structure,
 
205
                     * that determined from the "groups" vector, and that determined from the "symbols" string.
 
206
                     * The "ctrls" structure is considered less reliable because it has been observed to be incorrect. */
 
207
                    if ((xkb->group_count != symbol_group_number)
 
208
                    || (xkb->group_count != xkb_desc->ctrls->num_groups))
 
209
                    {
 
210
                        //g_warning("Group count mismatch, ctrls = %d, groups = %d, symbols = %d\n", xkb_desc->ctrls->num_groups, xkb->group_count, symbol_group_number);
 
211
 
 
212
                        /* Maximize the "groups" and "symbols" value. */
 
213
                        if (xkb->group_count < symbol_group_number)
 
214
                            xkb->group_count = symbol_group_number;
 
215
                    }
 
216
                    XFree(symbol_string);
 
217
                }
 
218
            }
 
219
        }
 
220
        XkbFreeKeyboard(xkb_desc, 0, True);
 
221
    }
 
222
 
 
223
    /* Ensure that all elements within the name vectors are initialized. */
 
224
    int i;
 
225
    for (i = 0; i < XkbNumKbdGroups; i += 1)
 
226
    {
 
227
        if (xkb->group_names[i] == NULL)
 
228
            xkb->group_names[i] = g_strdup("Unknown");
 
229
        if (xkb->symbol_names[i] == NULL)
 
230
            xkb->symbol_names[i] = g_strdup("None");
 
231
    }
 
232
 
 
233
    /* Create or recreate hash table */
 
234
    if (xkb->p_hash_table_group != NULL)
 
235
        g_hash_table_destroy(xkb->p_hash_table_group);
 
236
    xkb->p_hash_table_group = g_hash_table_new(g_direct_hash, NULL);
 
237
 
 
238
    return TRUE;
 
239
}
 
240
 
 
241
/* GDK event filter that receives events from all windows and the Xkb extension. */
 
242
static GdkFilterReturn xkb_event_filter(GdkXEvent * xevent, GdkEvent * event, XkbPlugin * xkb)
 
243
{
 
244
    XEvent * ev = (XEvent *) xevent;
 
245
 
 
246
    if (ev->xany.type == xkb->base_event_code + XkbEventCode)
 
247
    {
 
248
        /* Xkb event. */
 
249
        XkbEvent * xkbev = (XkbEvent *) ev;
 
250
        if (xkbev->any.xkb_type == XkbNewKeyboardNotify)
 
251
        {
 
252
            if(xkb_new_kbd_notify_ignore == NEW_KBD_STATE_NOTIFY_IGNORE_NO)
 
253
            {
 
254
                //g_print("xkb_new_kbd_notify_ignore == NEW_KBD_STATE_NOTIFY_IGNORE_NO\n");
 
255
                xkb_new_kbd_notify_ignore = NEW_KBD_STATE_NOTIFY_IGNORE_YES_SET;
 
256
                (void)g_timeout_add(1000/*msec*/, xkb_new_kbd_notify_ignore_slot, NULL);
 
257
                xkb_setxkbmap(xkb);
 
258
            }
 
259
            else if(xkb_new_kbd_notify_ignore == NEW_KBD_STATE_NOTIFY_IGNORE_YES_SET)
 
260
            {
 
261
                //g_print("xkb_new_kbd_notify_ignore == NEW_KBD_STATE_NOTIFY_IGNORE_YES_SET\n");
 
262
                xkb_new_kbd_notify_ignore = NEW_KBD_STATE_NOTIFY_IGNORE_YES_ALL;
 
263
                initialize_keyboard_description(xkb);
 
264
                refresh_group_xkb(xkb);
 
265
                xkb_redraw(xkb);
 
266
                xkb_enter_locale_by_process(xkb);
 
267
            }
 
268
        }
 
269
        else if (xkbev->any.xkb_type == XkbStateNotify)
 
270
        {
 
271
            if (xkbev->state.group != xkb->current_group_xkb_no)
 
272
            {
 
273
                /* Switch to the new group and redraw the display.
 
274
                 * This shouldn't be necessary, but mask the group number down for safety. */
 
275
                xkb->current_group_xkb_no = xkbev->state.group & (XkbNumKbdGroups - 1);
 
276
                refresh_group_xkb(xkb);
 
277
                xkb_redraw(xkb);
 
278
                xkb_enter_locale_by_process(xkb);
 
279
            }
 
280
        }
 
281
    }
 
282
    return GDK_FILTER_CONTINUE;
 
283
}
 
284
 
 
285
/* Initialize the Xkb interface. */
 
286
void xkb_mechanism_constructor(XkbPlugin * xkb)
 
287
{
 
288
    /* Initialize Xkb extension. */
 
289
    int opcode;
 
290
    int maj = XkbMajorVersion;
 
291
    int min = XkbMinorVersion;
 
292
    if ((XkbLibraryVersion(&maj, &min))
 
293
    && (XkbQueryExtension(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
 
294
                          &opcode, &xkb->base_event_code, &xkb->base_error_code, &maj, &min)))
 
295
    {
 
296
        Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
 
297
 
 
298
        /* Read the keyboard description. */
 
299
        initialize_keyboard_description(xkb);
 
300
 
 
301
        /* Establish GDK event filter. */
 
302
        gdk_window_add_filter(NULL, (GdkFilterFunc) xkb_event_filter, (gpointer) xkb);
 
303
 
 
304
        /* Specify events we will receive. */
 
305
        XkbSelectEvents(xdisplay, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask);
 
306
        XkbSelectEventDetails(xdisplay, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);
 
307
 
 
308
        /* Get current state. */
 
309
        refresh_group_xkb(xkb);
 
310
    }
 
311
}
 
312
 
 
313
/* Deallocate resources associated with Xkb interface. */
 
314
void xkb_mechanism_destructor(XkbPlugin * xkb)
 
315
{
 
316
    /* Remove event filter. */
 
317
    gdk_window_remove_filter(NULL, (GdkFilterFunc) xkb_event_filter, xkb);
 
318
 
 
319
    /* Free group and symbol name memory. */
 
320
    int i;
 
321
    for (i = 0; i < XkbNumKbdGroups; i++)
 
322
    {
 
323
        if (xkb->group_names[i] != NULL)
 
324
        {
 
325
            g_free(xkb->group_names[i]);
 
326
            xkb->group_names[i] = NULL;
 
327
        }
 
328
        if (xkb->symbol_names[i] != NULL)
 
329
        {
 
330
            g_free(xkb->symbol_names[i]);
 
331
            xkb->symbol_names[i] = NULL;
 
332
        }
 
333
    }
 
334
 
 
335
    /* Destroy the hash table. */
 
336
    g_hash_table_destroy(xkb->p_hash_table_group);
 
337
    xkb->p_hash_table_group = NULL;
 
338
}
 
339
 
 
340
/* Set the layout to the next layout. */
 
341
int xkb_change_group(XkbPlugin * xkb, int increment)
 
342
{
 
343
    /* Apply the increment and wrap the result. */
 
344
    int next_group = xkb->current_group_xkb_no + increment;
 
345
    if (next_group < 0) next_group = xkb->group_count - 1;
 
346
    if (next_group >= xkb->group_count) next_group = 0;
 
347
 
 
348
    /* Execute the change. */
 
349
    XkbLockGroup(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), XkbUseCoreKbd, next_group);
 
350
    refresh_group_xkb(xkb);
 
351
    xkb_redraw(xkb);
 
352
    xkb_enter_locale_by_process(xkb);
 
353
    return 1;
 
354
}
 
355
 
 
356
/* React to change of focus by switching to the application's layout or the default layout. */
 
357
void xkb_active_window_changed(XkbPlugin * xkb, Window window)
 
358
{
 
359
    gint  new_group_xkb_no = 0;
 
360
 
 
361
    gpointer pKey = 0, pVal = 0;
 
362
    if ((xkb->p_hash_table_group != NULL) && (g_hash_table_lookup_extended(xkb->p_hash_table_group, GINT_TO_POINTER(window), &pKey, &pVal)))
 
363
        new_group_xkb_no = GPOINTER_TO_INT(pVal);
 
364
 
 
365
    if (new_group_xkb_no < xkb->group_count)
 
366
    {
 
367
        XkbLockGroup(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
 
368
                     XkbUseCoreKbd, new_group_xkb_no);
 
369
        refresh_group_xkb(xkb);
 
370
    }
 
371
}