ardour
gtk_pianokeyboard.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2007, 2008 Edward Tomasz NapieraĹ‚a <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * This is piano_keyboard, piano keyboard-like GTK+ widget. It contains
29  * no MIDI-specific code.
30  *
31  * For questions and comments, contact Edward Tomasz Napierala <trasz@FreeBSD.org>.
32  */
33 
34 #include <assert.h>
35 #include <string.h>
36 #include <stdint.h>
37 #include <cairo/cairo.h>
38 
39 #include <gtk/gtk.h>
40 #include <gdk/gdkkeysyms.h>
41 
42 #include "gtk_pianokeyboard.h"
43 
44 #define PIANO_KEYBOARD_DEFAULT_WIDTH 730
45 #define PIANO_KEYBOARD_DEFAULT_HEIGHT 70
46 
47 enum {
52 };
53 
54 static guint piano_keyboard_signals[LAST_SIGNAL] = { 0 };
55 
56 static void
58 {
59  int w = pk->notes[0].w;
60  int h = pk->notes[0].h;
61 
62  int first_note_in_lower_row = (pk->octave + 5) * 12;
63  int last_note_in_lower_row = (pk->octave + 6) * 12 - 1;
64  int first_note_in_higher_row = (pk->octave + 6) * 12;
65  int last_note_in_higher_row = (pk->octave + 7) * 12 + 4;
66 
67  cairo_set_source_rgb (cr, 1.0f, 0.0f, 0.0f);
68  cairo_move_to (cr, pk->notes[first_note_in_lower_row].x + 3, h - 6);
69  cairo_line_to (cr, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
70  cairo_stroke (cr);
71 
72  cairo_set_source_rgb (cr, 0.0f, 0.0f, 1.0f);
73  cairo_move_to (cr, pk->notes[first_note_in_higher_row].x + 3, h - 9);
74  cairo_line_to (cr, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
75  cairo_stroke (cr);
76 }
77 
78 static void
80 {
81  GdkWindow* w = GTK_WIDGET(pk)->window;
82 
83  if (w) {
84  GdkRectangle r;
85 
86  r.x = pk->notes[note].x;
87  r.y = 0;
88  r.width = pk->notes[note].w;
89  r.height = pk->notes[note].h;
90 
91  gdk_window_invalidate_rect (w, &r, TRUE);
92  }
93 }
94 
95 static void
96 draw_note(PianoKeyboard *pk, cairo_t* cr, int note)
97 {
98  int is_white = pk->notes[note].white;
99 
100  int x = pk->notes[note].x;
101  int w = pk->notes[note].w;
102  int h = pk->notes[note].h;
103 
104  if (pk->notes[note].pressed || pk->notes[note].sustained) {
105  if (is_white) {
106  cairo_set_source_rgb (cr, 0.60f, 0.60f, 0.60f);
107  } else {
108  cairo_set_source_rgb (cr, 0.50f, 0.50f, 0.50f);
109  }
110  } else {
111  if (is_white) {
112  cairo_set_source_rgb (cr, 1.0f, 1.0f, 1.0f);
113  } else {
114  cairo_set_source_rgb (cr, 0.0f, 0.0f, 0.0f);
115  }
116  }
117 
118  cairo_set_line_width (cr, 1.0);
119 
120  cairo_rectangle (cr, x, 0, w, h);
121  cairo_fill (cr);
122 
123  cairo_set_source_rgb(cr, 0.0f, 0.0f, 0.0f); /* black outline */
124  cairo_rectangle (cr, x, 0, w, h);
125  cairo_stroke (cr);
126 
127  if (pk->enable_keyboard_cue) {
128  draw_keyboard_cue (pk, cr);
129  }
130 
131  /* We need to redraw black keys that partially obscure the white one. */
132  if (note < NNOTES - 2 && !pk->notes[note + 1].white) {
133  draw_note(pk, cr, note + 1);
134  }
135 
136  if (note > 0 && !pk->notes[note - 1].white) {
137  draw_note(pk, cr, note - 1);
138  }
139 }
140 
141 static int
143 {
144  assert(key >= 0);
145  assert(key < NNOTES);
146 
148 
149  /* This is for keyboard autorepeat protection. */
150  if (pk->notes[key].pressed)
151  return 0;
152 
153  if (pk->sustain_new_notes)
154  pk->notes[key].sustained = 1;
155  else
156  pk->notes[key].sustained = 0;
157 
158  if (pk->monophonic && pk->last_key != key) {
159  pk->notes[pk->last_key].pressed = 0;
160  pk->notes[pk->last_key].sustained = 0;
161  queue_note_draw(pk, pk->last_key);
162  }
163  pk->last_key = key;
164 
165  pk->notes[key].pressed = 1;
166 
167  g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key);
168  queue_note_draw(pk, key);
169 
170  return 1;
171 }
172 
173 static int
175 {
176  assert(key >= 0);
177  assert(key < NNOTES);
178 
180 
181  if (!pk->notes[key].pressed)
182  return 0;
183 
184  if (pk->sustain_new_notes)
185  pk->notes[key].sustained = 1;
186 
187  pk->notes[key].pressed = 0;
188 
189  if (pk->notes[key].sustained)
190  return 0;
191 
192  g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", key);
193  queue_note_draw(pk, key);
194 
195  return 1;
196 }
197 
198 static void
200 {
201  g_signal_emit_by_name(GTK_WIDGET(pk), "rest");
202 }
203 
204 static void
206 {
207  int i;
208 
209  for (i = 0; i < NNOTES; i++) {
210  if (pk->notes[i].pressed && !pk->notes[i].sustained) {
211  pk->notes[i].pressed = 0;
212  g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
213  queue_note_draw(pk, i);
214  }
215  }
216 }
217 
218 static void
220 {
221  int i;
222 
223  for (i = 0; i < NNOTES; i++) {
224  if (pk->notes[i].sustained) {
225  pk->notes[i].pressed = 0;
226  pk->notes[i].sustained = 0;
227  g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
228  queue_note_draw(pk, i);
229  }
230  }
231 }
232 
233 static int
234 key_binding(PianoKeyboard *pk, const char *key)
235 {
236  gpointer notused, note;
237  gboolean found;
238 
239  assert(pk->key_bindings != NULL);
240 
241  found = g_hash_table_lookup_extended(pk->key_bindings, key, &notused, &note);
242 
243  if (!found)
244  return -1;
245 
246  return (intptr_t)note;
247 }
248 
249 static void
250 bind_key(PianoKeyboard *pk, const char *key, int note)
251 {
252  assert(pk->key_bindings != NULL);
253 
254  g_hash_table_insert(pk->key_bindings, (const gpointer)key, (gpointer)((intptr_t)note));
255 }
256 
257 static void
259 {
260  assert(pk->key_bindings != NULL);
261 
262  g_hash_table_remove_all(pk->key_bindings);
263 }
264 
265 static void
267 {
268  clear_notes(pk);
269 
270  bind_key(pk, "space", 128);
271 
272  /* Lower keyboard row - "zxcvbnm". */
273  bind_key(pk, "z", 12); /* C0 */
274  bind_key(pk, "s", 13);
275  bind_key(pk, "x", 14);
276  bind_key(pk, "d", 15);
277  bind_key(pk, "c", 16);
278  bind_key(pk, "v", 17);
279  bind_key(pk, "g", 18);
280  bind_key(pk, "b", 19);
281  bind_key(pk, "h", 20);
282  bind_key(pk, "n", 21);
283  bind_key(pk, "j", 22);
284  bind_key(pk, "m", 23);
285 
286  /* Upper keyboard row, first octave - "qwertyu". */
287  bind_key(pk, "q", 24);
288  bind_key(pk, "2", 25);
289  bind_key(pk, "w", 26);
290  bind_key(pk, "3", 27);
291  bind_key(pk, "e", 28);
292  bind_key(pk, "r", 29);
293  bind_key(pk, "5", 30);
294  bind_key(pk, "t", 31);
295  bind_key(pk, "6", 32);
296  bind_key(pk, "y", 33);
297  bind_key(pk, "7", 34);
298  bind_key(pk, "u", 35);
299 
300  /* Upper keyboard row, the rest - "iop". */
301  bind_key(pk, "i", 36);
302  bind_key(pk, "9", 37);
303  bind_key(pk, "o", 38);
304  bind_key(pk, "0", 39);
305  bind_key(pk, "p", 40);
306 }
307 
308 static void
310 {
311  bind_keys_qwerty(pk);
312 
313  /* The only difference between QWERTY and QWERTZ is that the "y" and "z" are swapped together. */
314  bind_key(pk, "y", 12);
315  bind_key(pk, "z", 33);
316 }
317 
318 static void
320 {
321  clear_notes(pk);
322 
323  bind_key(pk, "space", 128);
324 
325  /* Lower keyboard row - "wxcvbn,". */
326  bind_key(pk, "w", 12); /* C0 */
327  bind_key(pk, "s", 13);
328  bind_key(pk, "x", 14);
329  bind_key(pk, "d", 15);
330  bind_key(pk, "c", 16);
331  bind_key(pk, "v", 17);
332  bind_key(pk, "g", 18);
333  bind_key(pk, "b", 19);
334  bind_key(pk, "h", 20);
335  bind_key(pk, "n", 21);
336  bind_key(pk, "j", 22);
337  bind_key(pk, "comma", 23);
338 
339  /* Upper keyboard row, first octave - "azertyu". */
340  bind_key(pk, "a", 24);
341  bind_key(pk, "eacute", 25);
342  bind_key(pk, "z", 26);
343  bind_key(pk, "quotedbl", 27);
344  bind_key(pk, "e", 28);
345  bind_key(pk, "r", 29);
346  bind_key(pk, "parenleft", 30);
347  bind_key(pk, "t", 31);
348  bind_key(pk, "minus", 32);
349  bind_key(pk, "y", 33);
350  bind_key(pk, "egrave", 34);
351  bind_key(pk, "u", 35);
352 
353  /* Upper keyboard row, the rest - "iop". */
354  bind_key(pk, "i", 36);
355  bind_key(pk, "ccedilla", 37);
356  bind_key(pk, "o", 38);
357  bind_key(pk, "agrave", 39);
358  bind_key(pk, "p", 40);
359 }
360 
361 static gint
362 keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
363 {
364  int note;
365  char *key;
366  guint keyval;
367  GdkKeymapKey kk;
368  PianoKeyboard *pk = PIANO_KEYBOARD(mk);
369 
370  (void) ignored;
371 
372  /* We're not using event->keyval, because we need keyval with level set to 0.
373  E.g. if user holds Shift and presses '7', we want to get a '7', not '&'. */
374  kk.keycode = event->hardware_keycode;
375  kk.level = 0;
376  kk.group = 0;
377 
378  keyval = gdk_keymap_lookup_key(NULL, &kk);
379 
380  key = gdk_keyval_name(gdk_keyval_to_lower(keyval));
381 
382  if (key == NULL) {
383  g_message("gtk_keyval_name() returned NULL; please report this.");
384  return FALSE;
385  }
386 
387  note = key_binding(pk, key);
388 
389  if (note < 0) {
390  /* Key was not bound. Maybe it's one of the keys handled in jack-keyboard.c. */
391  return FALSE;
392  }
393 
394  if (note == 128) {
395  if (event->type == GDK_KEY_RELEASE) {
396  rest (pk);
397  }
398 
399  return TRUE;
400  }
401 
402  note += pk->octave * 12;
403 
404  assert(note >= 0);
405  assert(note < NNOTES);
406 
407  if (event->type == GDK_KEY_PRESS) {
408  press_key(pk, note);
409 
410  } else if (event->type == GDK_KEY_RELEASE) {
411  release_key(pk, note);
412  }
413 
414  return TRUE;
415 }
416 
417 static int
418 get_note_for_xy(PianoKeyboard *pk, int x, int y)
419 {
420  int height = GTK_WIDGET(pk)->allocation.height;
421  int note;
422 
423  if (y <= height / 2) {
424  for (note = 0; note < NNOTES - 1; note++) {
425  if (pk->notes[note].white)
426  continue;
427 
428  if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
429  return note;
430  }
431  }
432 
433  for (note = 0; note < NNOTES - 1; note++) {
434  if (!pk->notes[note].white)
435  continue;
436 
437  if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
438  return note;
439  }
440 
441  return -1;
442 }
443 
444 static gboolean
445 mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ignored)
446 {
447  int x = event->x;
448  int y = event->y;
449 
450  int note = get_note_for_xy(pk, x, y);
451 
452  (void) ignored;
453 
454  if (event->button != 1)
455  return TRUE;
456 
457  if (event->type == GDK_BUTTON_PRESS) {
458  /* This is possible when you make the window a little wider and then click
459  on the grey area. */
460  if (note < 0) {
461  return TRUE;
462  }
463 
464  if (pk->note_being_pressed_using_mouse >= 0)
466 
467  press_key(pk, note);
469 
470  } else if (event->type == GDK_BUTTON_RELEASE) {
471  if (note >= 0) {
472  release_key(pk, note);
473 
474  } else {
475  if (pk->note_being_pressed_using_mouse >= 0)
477  }
478 
480 
481  }
482 
483  return TRUE;
484 }
485 
486 static gboolean
487 mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer ignored)
488 {
489  int note;
490 
491  (void) ignored;
492 
493  if ((event->state & GDK_BUTTON1_MASK) == 0)
494  return TRUE;
495 
496  note = get_note_for_xy(pk, event->x, event->y);
497 
498  if (note != pk->note_being_pressed_using_mouse && note >= 0) {
499 
500  if (pk->note_being_pressed_using_mouse >= 0)
502  press_key(pk, note);
504  }
505 
506  return TRUE;
507 }
508 
509 static gboolean
510 piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event)
511 {
512  int i;
513  PianoKeyboard *pk = PIANO_KEYBOARD(widget);
514  cairo_t* cr = gdk_cairo_create (GDK_DRAWABLE (GTK_WIDGET(pk)->window));
515 
516  gdk_cairo_region (cr, event->region);
517  cairo_clip (cr);
518 
519  for (i = 0; i < NNOTES; i++) {
520  GdkRectangle r;
521 
522  r.x = pk->notes[i].x;
523  r.y = 0;
524  r.width = pk->notes[i].w;
525  r.height = pk->notes[i].h;
526 
527  switch (gdk_region_rect_in (event->region, &r)) {
528  case GDK_OVERLAP_RECTANGLE_PART:
529  case GDK_OVERLAP_RECTANGLE_IN:
530  draw_note (pk, cr, i);
531  break;
532  default:
533  break;
534  }
535  }
536 
537  cairo_destroy (cr);
538 
539  return TRUE;
540 }
541 
542 static void
543 piano_keyboard_size_request(GtkWidget* w, GtkRequisition *requisition)
544 {
545  (void) w;
546 
547  requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH;
548  requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
549 }
550 
551 static void
553 {
554  int number_of_white_keys = (NNOTES - 1) * (7.0 / 12.0);
555 
556  int key_width;
557  int black_key_width;
558  int useful_width;
559 
560  int note;
561  int white_key = 0;
562  int note_in_octave;
563 
564  int width = GTK_WIDGET(pk)->allocation.width;
565  int height = GTK_WIDGET(pk)->allocation.height;
566 
567  key_width = width / number_of_white_keys;
568  black_key_width = key_width * 0.8;
569  useful_width = number_of_white_keys * key_width;
570  pk->widget_margin = (width - useful_width) / 2;
571 
572  for (note = 0, white_key = 0; note < NNOTES - 2; note++) {
573  note_in_octave = note % 12;
574 
575  if (note_in_octave == 1 || note_in_octave == 3 || note_in_octave == 6 ||
576  note_in_octave == 8 || note_in_octave == 10) {
577 
578  /* This note is black key. */
579  pk->notes[note].x = pk->widget_margin + white_key * key_width - black_key_width / 2;
580  pk->notes[note].w = black_key_width;
581  pk->notes[note].h = height / 2;
582  pk->notes[note].white = 0;
583 
584  continue;
585  }
586 
587  /* This note is white key. */
588  pk->notes[note].x = pk->widget_margin + white_key * key_width;
589  pk->notes[note].w = key_width;
590  pk->notes[note].h = height;
591  pk->notes[note].white = 1;
592 
593  white_key++;
594  }
595 }
596 
597 static void
598 piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
599 {
600  /* XXX: Are these two needed? */
601  g_return_if_fail(widget != NULL);
602  g_return_if_fail(allocation != NULL);
603 
604  widget->allocation = *allocation;
605 
607 
608  if (GTK_WIDGET_REALIZED(widget)) {
609  gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
610  }
611 }
612 
613 static void
615 {
616  GtkWidgetClass *widget_klass;
617 
618  /* Set up signals. */
619  piano_keyboard_signals[NOTE_ON_SIGNAL] = g_signal_new ("note-on",
620  G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
621  0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
622 
623  piano_keyboard_signals[NOTE_OFF_SIGNAL] = g_signal_new ("note-off",
624  G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
625  0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
626 
627  piano_keyboard_signals[REST_SIGNAL] = g_signal_new ("rest",
628  G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
629  0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
630 
631  widget_klass = (GtkWidgetClass*) klass;
632 
633  widget_klass->expose_event = piano_keyboard_expose;
634  widget_klass->size_request = piano_keyboard_size_request;
635  widget_klass->size_allocate = piano_keyboard_size_allocate;
636 }
637 
638 static void
639 piano_keyboard_init(GtkWidget *mk)
640 {
641  gtk_widget_add_events(mk, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
642 
643  g_signal_connect(G_OBJECT(mk), "button-press-event", G_CALLBACK(mouse_button_event_handler), NULL);
644  g_signal_connect(G_OBJECT(mk), "button-release-event", G_CALLBACK(mouse_button_event_handler), NULL);
645  g_signal_connect(G_OBJECT(mk), "motion-notify-event", G_CALLBACK(mouse_motion_event_handler), NULL);
646  g_signal_connect(G_OBJECT(mk), "key-press-event", G_CALLBACK(keyboard_event_handler), NULL);
647  g_signal_connect(G_OBJECT(mk), "key-release-event", G_CALLBACK(keyboard_event_handler), NULL);
648 }
649 
650 GType
652 {
653  static GType mk_type = 0;
654 
655  if (!mk_type) {
656  static const GTypeInfo mk_info = {
657  sizeof(PianoKeyboardClass),
658  NULL, /* base_init */
659  NULL, /* base_finalize */
660  (GClassInitFunc) piano_keyboard_class_init,
661  NULL, /* class_finalize */
662  NULL, /* class_data */
663  sizeof (PianoKeyboard),
664  0, /* n_preallocs */
665  (GInstanceInitFunc) piano_keyboard_init,
666  0, /* value_table */
667  };
668 
669  mk_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "PianoKeyboard", &mk_info, (GTypeFlags)0);
670  }
671 
672  return mk_type;
673 }
674 
675 GtkWidget *
677 {
678  GtkWidget *widget = (GtkWidget*)gtk_type_new(piano_keyboard_get_type());
679 
680  PianoKeyboard *pk = PIANO_KEYBOARD(widget);
681 
683  pk->sustain_new_notes = 0;
684  pk->enable_keyboard_cue = 0;
685  pk->octave = 4;
687  pk->last_key = 0;
688  pk->monophonic = FALSE;
689  memset((void *)pk->notes, 0, sizeof(struct PKNote) * NNOTES);
690  pk->key_bindings = g_hash_table_new(g_str_hash, g_str_equal);
691  bind_keys_qwerty(pk);
692 
693  return widget;
694 }
695 
696 void
698 {
699  pk->enable_keyboard_cue = enabled;
700 }
701 
702 void
704 {
705  pk->monophonic = monophonic;
706 }
707 
708 void
710 {
711  if (!pk->sustain_new_notes) {
712  pk->sustain_new_notes = 1;
714  }
715 }
716 
717 void
719 {
722 
723  pk->sustain_new_notes = 0;
724 }
725 
726 void
728 {
729  if (pk->notes[note].pressed == 0) {
730  pk->notes[note].pressed = 1;
731  queue_note_draw (pk, note);
732  }
733 }
734 
735 void
737 {
738  if (pk->notes[note].pressed || pk->notes[note].sustained) {
739  pk->notes[note].pressed = 0;
740  pk->notes[note].sustained = 0;
741  queue_note_draw (pk, note);
742  }
743 }
744 
745 void
747 {
749  pk->octave = octave;
750  gtk_widget_queue_draw(GTK_WIDGET(pk));
751 }
752 
753 gboolean
755 {
756  assert(layout);
757 
758  if (!g_ascii_strcasecmp(layout, "QWERTY")) {
759  bind_keys_qwerty(pk);
760 
761  } else if (!g_ascii_strcasecmp(layout, "QWERTZ")) {
762  bind_keys_qwertz(pk);
763 
764  } else if (!g_ascii_strcasecmp(layout, "AZERTY")) {
765  bind_keys_azerty(pk);
766 
767  } else {
768  /* Unknown layout name. */
769  return TRUE;
770  }
771 
772  return FALSE;
773 }
static void stop_unsustained_notes(PianoKeyboard *pk)
static void clear_notes(PianoKeyboard *pk)
static void piano_keyboard_class_init(PianoKeyboardClass *klass)
static int key_binding(PianoKeyboard *pk, const char *key)
void piano_keyboard_sustain_release(PianoKeyboard *pk)
void piano_keyboard_set_monophonic(PianoKeyboard *pk, gboolean monophonic)
static void stop_sustained_notes(PianoKeyboard *pk)
static gboolean mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ignored)
tuple f
Definition: signals.py:35
static guint piano_keyboard_signals[LAST_SIGNAL]
gboolean piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout)
GtkWidget * piano_keyboard_new(void)
static void rest(PianoKeyboard *pk)
static void piano_keyboard_init(GtkWidget *mk)
static void draw_keyboard_cue(PianoKeyboard *pk, cairo_t *cr)
#define PIANO_KEYBOARD(obj)
GHashTable * key_bindings
#define PIANO_KEYBOARD_DEFAULT_HEIGHT
void piano_keyboard_set_note_off(PianoKeyboard *pk, int note)
static void bind_keys_qwerty(PianoKeyboard *pk)
static gint keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
static int get_note_for_xy(PianoKeyboard *pk, int x, int y)
struct _PianoKeyboardClass PianoKeyboardClass
struct _PianoKeyboard PianoKeyboard
static void bind_keys_azerty(PianoKeyboard *pk)
int intptr_t
Definition: types.h:46
static void bind_keys_qwertz(PianoKeyboard *pk)
static void bind_key(PianoKeyboard *pk, const char *key, int note)
void piano_keyboard_set_note_on(PianoKeyboard *pk, int note)
void piano_keyboard_sustain_press(PianoKeyboard *pk)
#define PIANO_KEYBOARD_DEFAULT_WIDTH
GType piano_keyboard_get_type(void)
static void draw_note(PianoKeyboard *pk, cairo_t *cr, int note)
static void queue_note_draw(PianoKeyboard *pk, int note)
static gboolean piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event)
static int release_key(PianoKeyboard *pk, int key)
static void piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
#define NNOTES
static gboolean mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer ignored)
static int press_key(PianoKeyboard *pk, int key)
static void piano_keyboard_size_request(GtkWidget *w, GtkRequisition *requisition)
void piano_keyboard_set_keyboard_cue(PianoKeyboard *pk, int enabled)
void piano_keyboard_set_octave(PianoKeyboard *pk, int octave)
static void recompute_dimensions(PianoKeyboard *pk)
volatile struct PKNote notes[NNOTES]
int note_being_pressed_using_mouse