shortcuts.c 13.7 KB
Newer Older
Julien Bonjean's avatar
Julien Bonjean committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc.
Julien Bonjean's avatar
Julien Bonjean committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *  Author: Julien Bonjean <julien.bonjean@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 19 20 21 22 23 24 25 26 27 28
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
Julien Bonjean's avatar
Julien Bonjean committed
29 30 31 32 33 34 35 36 37
 */

#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <X11/Xlib.h>
#include <X11/XF86keysym.h>
#include <gdk/gdkx.h>
#include <dbus/dbus-glib.h>
38 39
#include <stdlib.h>
#include <stdio.h>
Julien Bonjean's avatar
Julien Bonjean committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

#include "shortcuts.h"
#include "mainwindow.h"
#include "callable_obj.h"
#include "sflphone_const.h"
#include "dbus.h"

// used to store accelerator config
static Accelerator* accelerators_list;

// used to store config (for dbus calls)
static GHashTable* shortcutsMap;

/*
 * Callbacks
 */

57 58 59 60 61 62
static void
toggle_pick_up_hang_up_callback ()
{
  callable_obj_t * selectedCall = calltab_get_selected_call (active_calltree);
  conference_obj_t * selectedConf = calltab_get_selected_conf (active_calltree);

63
  g_print ("toggle_pick_up_hang_up_callback\n");
64 65 66 67 68 69 70 71 72

  if (selectedCall)
    {
      switch (selectedCall->_state)
        {
      case CALL_STATE_INCOMING:
      case CALL_STATE_TRANSFERT:
        sflphone_pick_up ();
        break;
73
      case CALL_STATE_DIALING:
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
      case CALL_STATE_HOLD:
      case CALL_STATE_CURRENT:
      case CALL_STATE_RECORD:
      case CALL_STATE_RINGING:
        sflphone_hang_up ();
        break;
        }
    }
  else if (selectedConf)
    {
      dbus_hang_up_conference (selectedConf);
    }
  else
    sflphone_pick_up ();
}

Julien Bonjean's avatar
Julien Bonjean committed
90
static void
91
pick_up_callback ()
Julien Bonjean's avatar
Julien Bonjean committed
92
{
93
  sflphone_pick_up ();
Julien Bonjean's avatar
Julien Bonjean committed
94 95 96
}

static void
97
hang_up_callback ()
Julien Bonjean's avatar
Julien Bonjean committed
98
{
99
  sflphone_hang_up ();
Julien Bonjean's avatar
Julien Bonjean committed
100 101
}

102 103 104 105 106 107 108 109 110 111 112 113
static void
toggle_hold_callback ()
{
  callable_obj_t * selectedCall = calltab_get_selected_call (current_calls);
  conference_obj_t * selectedConf = calltab_get_selected_conf (active_calltree);

  if (selectedCall)
    {
      switch (selectedCall->_state)
        {
      case CALL_STATE_CURRENT:
      case CALL_STATE_RECORD:
114 115
        g_print ("on hold\n");
        sflphone_on_hold ();
116 117
        break;
      case CALL_STATE_HOLD:
118 119
        g_print ("off hold\n");
        sflphone_off_hold ();
120 121 122 123 124 125 126 127 128
        break;
        }
    }
  else if (selectedConf)
    dbus_hold_conference (selectedConf);
  else
    ERROR("Should not happen");
}

Julien Bonjean's avatar
Julien Bonjean committed
129
static void
130
popup_window_callback ()
Julien Bonjean's avatar
Julien Bonjean committed
131
{
132 133
  gtk_widget_hide (GTK_WIDGET(get_main_window()));
  gtk_widget_show (GTK_WIDGET(get_main_window()));
134 135
  //gtk_window_move (GTK_WINDOW (get_main_window ()),
  //    dbus_get_window_position_x (), dbus_get_window_position_y ());
Julien Bonjean's avatar
Julien Bonjean committed
136 137 138
}

static void
139
default_callback ()
Julien Bonjean's avatar
Julien Bonjean committed
140 141 142 143 144 145 146 147
{
  ERROR("Missing shortcut callback");
}

/*
 * return callback corresponding to a specific action
 */
static void*
148
get_action_callback (const gchar* action)
Julien Bonjean's avatar
Julien Bonjean committed
149
{
150
  if (strcmp (action, SHORTCUT_PICKUP) == 0)
Julien Bonjean's avatar
Julien Bonjean committed
151 152
    return pick_up_callback;

153
  if (strcmp (action, SHORTCUT_HANGUP) == 0)
Julien Bonjean's avatar
Julien Bonjean committed
154 155
    return hang_up_callback;

156
  if (strcmp (action, SHORTCUT_POPUP) == 0)
Julien Bonjean's avatar
Julien Bonjean committed
157 158
    return popup_window_callback;

159
  if (strcmp (action, SHORTCUT_TOGGLEPICKUPHANGUP) == 0)
160 161
    return toggle_pick_up_hang_up_callback;

162
  if (strcmp (action, SHORTCUT_TOGGLEHOLD) == 0)
163 164
    return toggle_hold_callback;

Julien Bonjean's avatar
Julien Bonjean committed
165 166 167 168 169 170 171 172 173 174 175
  return default_callback;
}

/*
 * Handle bindings
 */

/*
 * Remove all existing bindings
 */
static void
176
remove_bindings ()
Julien Bonjean's avatar
Julien Bonjean committed
177
{
178 179 180 181
  GdkDisplay *display = NULL;
  GdkScreen *screen = NULL;
  GdkWindow *root = NULL;
  int i, j = 0;
Julien Bonjean's avatar
Julien Bonjean committed
182

183
  display = gdk_display_get_default ();
Julien Bonjean's avatar
Julien Bonjean committed
184

185
  for (i = 0; i < gdk_display_get_n_screens (display); i++)
Julien Bonjean's avatar
Julien Bonjean committed
186
    {
187 188
      screen = gdk_display_get_screen (display, i);
      if (screen != NULL)
Julien Bonjean's avatar
Julien Bonjean committed
189
        {
190 191 192 193 194
          j = 0;
          root = gdk_screen_get_root_window (screen);

          // remove filter
          gdk_window_remove_filter (root, filter_keys, NULL);
Julien Bonjean's avatar
Julien Bonjean committed
195

196 197 198 199
          // unbind shortcuts
          while (accelerators_list[j].action != NULL)
            {
              if (accelerators_list[j].key != 0)
Julien Bonjean's avatar
Julien Bonjean committed
200
                {
201 202
                  ungrab_key (accelerators_list[j].key,
                      accelerators_list[j].mask, root);
Julien Bonjean's avatar
Julien Bonjean committed
203
                }
204
              j++;
Julien Bonjean's avatar
Julien Bonjean committed
205 206 207 208 209 210 211 212 213
            }
        }
    }
}

/*
 * Create all bindings, using stored configuration
 */
static void
214
create_bindings ()
Julien Bonjean's avatar
Julien Bonjean committed
215 216 217 218
{
  GdkDisplay *display;
  GdkScreen *screen;
  GdkWindow *root;
219
  int i, j = 0;
Julien Bonjean's avatar
Julien Bonjean committed
220

221
  display = gdk_display_get_default ();
Julien Bonjean's avatar
Julien Bonjean committed
222

223
  for (i = 0; i < gdk_display_get_n_screens (display); i++)
Julien Bonjean's avatar
Julien Bonjean committed
224
    {
225 226
      screen = gdk_display_get_screen (display, i);
      if (screen != NULL)
Julien Bonjean's avatar
Julien Bonjean committed
227
        {
228 229
          j = 0;
          root = gdk_screen_get_root_window (screen);
Julien Bonjean's avatar
Julien Bonjean committed
230

231 232 233 234 235 236 237
          // add filter
          gdk_window_add_filter (root, filter_keys, NULL);

          // bind shortcuts
          while (accelerators_list[j].action != NULL)
            {
              if (accelerators_list[j].key != 0)
Julien Bonjean's avatar
Julien Bonjean committed
238
                {
239 240
                  grab_key (accelerators_list[j].key,
                      accelerators_list[j].mask, root);
Julien Bonjean's avatar
Julien Bonjean committed
241
                }
242
              j++;
Julien Bonjean's avatar
Julien Bonjean committed
243 244 245 246 247 248 249 250 251
            }
        }
    }
}

/*
 * Initialize a specific binding
 */
static void
252
initialize_binding (const gchar* action, guint key, GdkModifierType mask)
Julien Bonjean's avatar
Julien Bonjean committed
253
{
254 255 256
  int i = 0;

  while (accelerators_list[i].action != NULL)
Julien Bonjean's avatar
Julien Bonjean committed
257
    {
258
      if (strcmp (action, accelerators_list[i].action) == 0)
Julien Bonjean's avatar
Julien Bonjean committed
259 260 261
        {
          break;
        }
262
      i++;
Julien Bonjean's avatar
Julien Bonjean committed
263 264
    }

265
  if (accelerators_list[i].action == NULL)
Julien Bonjean's avatar
Julien Bonjean committed
266 267 268 269 270 271
    {
      ERROR("Should not happen: cannot find corresponding action");
      return;
    }

  // update config value
272 273
  accelerators_list[i].key = key;
  accelerators_list[i].mask = mask;
Julien Bonjean's avatar
Julien Bonjean committed
274 275

  // update bindings
276
  create_bindings ();
Julien Bonjean's avatar
Julien Bonjean committed
277 278 279 280 281 282
}

/*
 * Prepare accelerators list
 */
static void
283
initialize_accelerators_list ()
Julien Bonjean's avatar
Julien Bonjean committed
284
{
285 286 287 288
  GList* shortcutsKeysElement, *shortcutsKeys = NULL;
  int i = 0;

  shortcutsKeys = g_hash_table_get_keys (shortcutsMap);
Julien Bonjean's avatar
Julien Bonjean committed
289

290 291
  accelerators_list = (Accelerator*) malloc (
      (g_list_length (shortcutsKeys) + 1) * sizeof(Accelerator));
Julien Bonjean's avatar
Julien Bonjean committed
292 293 294 295 296 297

  for (shortcutsKeysElement = shortcutsKeys; shortcutsKeysElement; shortcutsKeysElement
      = shortcutsKeysElement->next)
    {
      gchar* action = shortcutsKeysElement->data;

298 299 300 301
      accelerators_list[i].action = g_strdup (action);
      accelerators_list[i].callback = get_action_callback (action);
      accelerators_list[i].mask = 0;
      accelerators_list[i].key = 0;
Julien Bonjean's avatar
Julien Bonjean committed
302

303
      i++;
Julien Bonjean's avatar
Julien Bonjean committed
304 305 306
    }

  // last element must be null
307 308 309 310
  accelerators_list[i].action = 0;
  accelerators_list[i].callback = 0;
  accelerators_list[i].mask = 0;
  accelerators_list[i].key = 0;
Julien Bonjean's avatar
Julien Bonjean committed
311 312
}

313
static void
314
update_shortcuts_map (const gchar* action, guint key, GdkModifierType mask)
315 316 317 318
{
  gchar buffer[7];

  // Bindings: MASKxCODE
319
  sprintf (buffer, "%dx%d", mask, key);
320 321 322 323 324

  g_hash_table_replace (shortcutsMap, g_strdup (action), g_strdup (buffer));
}

static void
325
update_bindings_data (guint index, guint key, GdkModifierType mask)
326
{
327 328
  int i = 0;

329 330 331 332
  // we need to be sure this code is not already affected
  // to another action
  while (accelerators_list[i].action != NULL)
    {
333 334
      if (accelerators_list[i].key == key && accelerators_list[i].mask == mask
          && accelerators_list[i].key != 0)
335
        {
336
          DEBUG("Existing mapping found %d+%d", mask, key);
337

338
          // disable old binding
339
          accelerators_list[i].key = 0;
340
          accelerators_list[i].mask = 0;
341 342

          // update config table
343
          update_shortcuts_map (accelerators_list[i].action, 0, 0);
344 345 346
        }
      i++;
    }
347

348 349
  // store new key
  accelerators_list[index].key = key;
350
  accelerators_list[index].mask = mask;
351 352

  // update value in hashtable (used for dbus calls)
353
  update_shortcuts_map (accelerators_list[index].action,
354
      accelerators_list[index].key, accelerators_list[index].mask);
355 356
}

Julien Bonjean's avatar
Julien Bonjean committed
357 358 359 360 361 362 363 364
/*
 * "Public" functions
 */

/*
 * Update current bindings with a new value
 */
void
365
shortcuts_update_bindings (guint index, guint key, GdkModifierType mask)
Julien Bonjean's avatar
Julien Bonjean committed
366 367
{
  // first remove all existing bindings
368
  remove_bindings ();
Julien Bonjean's avatar
Julien Bonjean committed
369

370
  // update data
371
  update_bindings_data (index, key, mask);
372

Julien Bonjean's avatar
Julien Bonjean committed
373
  // recreate all bindings
374
  create_bindings ();
Julien Bonjean's avatar
Julien Bonjean committed
375 376

  // update configuration
377
  dbus_set_shortcuts (shortcutsMap);
Julien Bonjean's avatar
Julien Bonjean committed
378 379 380 381 382 383
}

/*
 * Initialize bindings with configuration retrieved from dbus
 */
void
384
shortcuts_initialize_bindings ()
Julien Bonjean's avatar
Julien Bonjean committed
385
{
386 387 388 389
  GList* shortcutsKeys, *shortcutsKeysElement = NULL;
  gchar* action, *maskAndKey, *token1, *token2 = NULL;
  guint mask, key = 0;

Alexandre Savard's avatar
Alexandre Savard committed
390 391
  DEBUG("Shortcuts: Initialize bindings");

Julien Bonjean's avatar
Julien Bonjean committed
392
  // get shortcuts stored in config through dbus
393
  shortcutsMap = dbus_get_shortcuts ();
Julien Bonjean's avatar
Julien Bonjean committed
394 395

  // initialize list of keys
396
  initialize_accelerators_list ();
Julien Bonjean's avatar
Julien Bonjean committed
397 398

  // iterate through keys to initialize bindings
399
  shortcutsKeys = g_hash_table_get_keys (shortcutsMap);
400

Julien Bonjean's avatar
Julien Bonjean committed
401 402 403
  for (shortcutsKeysElement = shortcutsKeys; shortcutsKeysElement; shortcutsKeysElement
      = shortcutsKeysElement->next)
    {
404 405
      action = shortcutsKeysElement->data;
      maskAndKey = g_strdup (g_hash_table_lookup (shortcutsMap, action));
406

407 408
      token1 = strtok (maskAndKey, "x");
      token2 = strtok (NULL, "x");
409

410 411
      mask = 0;
      key = 0;
412 413

      // Value not setted
Alexandre Savard's avatar
Alexandre Savard committed
414
      if (token1 && token2){
Alexandre Savard's avatar
Alexandre Savard committed
415
	DEBUG("Ahortcuts: token1 %s, token2 %s", token1, token2);
Alexandre Savard's avatar
Alexandre Savard committed
416 417 418 419
	  
	mask = atoi (token1);
	key = atoi (token2);
      }
420

421 422
      if (key != 0)
        initialize_binding (action, key, mask);
Julien Bonjean's avatar
Julien Bonjean committed
423 424 425 426 427 428 429
    }
}

/*
 * Initialize bindings with configuration retrieved from dbus
 */
void
430
shortcuts_destroy_bindings ()
Julien Bonjean's avatar
Julien Bonjean committed
431
{
432 433
  int i = 0;

Julien Bonjean's avatar
Julien Bonjean committed
434
  // remove bindings
435
  remove_bindings ();
Julien Bonjean's avatar
Julien Bonjean committed
436 437

  // free pointers
438
  while (accelerators_list[i].action != NULL)
Julien Bonjean's avatar
Julien Bonjean committed
439
    {
440 441
      g_free (accelerators_list[i].action);
      i++;
Julien Bonjean's avatar
Julien Bonjean committed
442
    }
443

444
  free (accelerators_list);
Julien Bonjean's avatar
Julien Bonjean committed
445 446 447
}

Accelerator*
448
shortcuts_get_list ()
Julien Bonjean's avatar
Julien Bonjean committed
449 450 451 452 453 454 455 456 457 458 459 460
{
  return accelerators_list;
}

/*
 * XLib functions
 */

/*
 * filter used when an event is catched
 */
static GdkFilterReturn
461
filter_keys (const GdkXEvent *xevent, const GdkEvent *event, gpointer data)
Julien Bonjean's avatar
Julien Bonjean committed
462
{
463 464 465 466
  XEvent *xev = NULL;
  XKeyEvent *key = NULL;
  GdkModifierType keystate = 0;
  int i = 0;
Julien Bonjean's avatar
Julien Bonjean committed
467 468

  xev = (XEvent *) xevent;
Alexandre Savard's avatar
Alexandre Savard committed
469 470 471
  if (xev->type != KeyPress) {
    return GDK_FILTER_CONTINUE;
  }
Julien Bonjean's avatar
Julien Bonjean committed
472 473

  key = (XKeyEvent *) xevent;
474
  keystate = key->state & ~(Mod2Mask | Mod5Mask | LockMask);
Julien Bonjean's avatar
Julien Bonjean committed
475 476

  // try to find corresponding action
Alexandre Savard's avatar
Alexandre Savard committed
477
  while (accelerators_list[i].action != NULL) {
478 479
      if (accelerators_list[i].key == key->keycode && accelerators_list[i].mask
          == keystate)
Julien Bonjean's avatar
Julien Bonjean committed
480
        {
481
          DEBUG("catched key for action: %s", accelerators_list[i].action,
482
              accelerators_list[i].key);
Julien Bonjean's avatar
Julien Bonjean committed
483 484

          // call associated callback function
485
          accelerators_list[i].callback ();
Julien Bonjean's avatar
Julien Bonjean committed
486 487 488 489 490 491

          return GDK_FILTER_REMOVE;
        }
      i++;
    }

492 493
  DEBUG("Should not be reached");

Julien Bonjean's avatar
Julien Bonjean committed
494 495 496 497 498 499 500
  return GDK_FILTER_CONTINUE;
}

/*
 * Remove key "catcher" from GDK layer
 */
static void
501
ungrab_key (guint key, GdkModifierType mask, const GdkWindow *root)
Julien Bonjean's avatar
Julien Bonjean committed
502
{
503
  DEBUG("Ungrabbing key %d+%d", mask, key);
504

505 506
  gdk_error_trap_push ();

507 508 509 510 511
  XUngrabKey (GDK_DISPLAY (), key, mask, GDK_WINDOW_XID (root));
  XUngrabKey (GDK_DISPLAY (), key, Mod2Mask | mask, GDK_WINDOW_XID (root));
  XUngrabKey (GDK_DISPLAY (), key, Mod5Mask | mask, GDK_WINDOW_XID (root));
  XUngrabKey (GDK_DISPLAY (), key, LockMask | mask, GDK_WINDOW_XID (root));
  XUngrabKey (GDK_DISPLAY (), key, Mod2Mask | Mod5Mask | mask,
512
      GDK_WINDOW_XID (root));
513
  XUngrabKey (GDK_DISPLAY (), key, Mod2Mask | LockMask | mask,
514
      GDK_WINDOW_XID (root));
515
  XUngrabKey (GDK_DISPLAY (), key, Mod5Mask | LockMask | mask,
516
      GDK_WINDOW_XID (root));
517
  XUngrabKey (GDK_DISPLAY (), key, Mod2Mask | Mod5Mask | LockMask | mask,
518
      GDK_WINDOW_XID (root));
Julien Bonjean's avatar
Julien Bonjean committed
519

520 521
  gdk_flush ();
  if (gdk_error_trap_pop ())
Julien Bonjean's avatar
Julien Bonjean committed
522
    {
523
      DEBUG ( "Error ungrabbing key %d+%d", mask, key);
Julien Bonjean's avatar
Julien Bonjean committed
524 525 526 527 528 529 530
    }
}

/*
 * Add key "catcher" to GDK layer
 */
static void
531
grab_key (guint key, GdkModifierType mask, const GdkWindow *root)
Julien Bonjean's avatar
Julien Bonjean committed
532
{
533
  gdk_error_trap_push ();
Julien Bonjean's avatar
Julien Bonjean committed
534

535
  DEBUG("Grabbing key %d+%d", mask, key);
536

537 538 539 540 541 542 543
  XGrabKey (GDK_DISPLAY (), key, mask, GDK_WINDOW_XID (root), True,
      GrabModeAsync, GrabModeAsync);
  XGrabKey (GDK_DISPLAY (), key, Mod2Mask | mask, GDK_WINDOW_XID (root), True,
      GrabModeAsync, GrabModeAsync);
  XGrabKey (GDK_DISPLAY (), key, Mod5Mask | mask, GDK_WINDOW_XID (root), True,
      GrabModeAsync, GrabModeAsync);
  XGrabKey (GDK_DISPLAY (), key, LockMask | mask, GDK_WINDOW_XID (root), True,
Julien Bonjean's avatar
Julien Bonjean committed
544
      GrabModeAsync, GrabModeAsync);
545
  XGrabKey (GDK_DISPLAY (), key, Mod2Mask | Mod5Mask | mask,
546
      GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
547
  XGrabKey (GDK_DISPLAY (), key, Mod2Mask | LockMask | mask,
548
      GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
549
  XGrabKey (GDK_DISPLAY (), key, Mod5Mask | LockMask | mask,
550
      GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
551
  XGrabKey (GDK_DISPLAY (), key, Mod2Mask | Mod5Mask | LockMask | mask,
552
      GDK_WINDOW_XID (root), True, GrabModeAsync, GrabModeAsync);
Julien Bonjean's avatar
Julien Bonjean committed
553

554 555
  gdk_flush ();
  if (gdk_error_trap_pop ())
Julien Bonjean's avatar
Julien Bonjean committed
556
    {
557
      DEBUG ("Error grabbing key %d+%d", mask, key);
Julien Bonjean's avatar
Julien Bonjean committed
558 559
    }
}