/*-
- * Copyright (c) 2007, 2008 Edward Tomasz Napierała <trasz@FreeBSD.org>
+ * Copyright (c) 2007, 2008 Edward Tomasz Napiera�ła <trasz@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include <assert.h>
#include <string.h>
-#include <strings.h>
#include <stdint.h>
+#include <cairo/cairo.h>
+
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
enum {
NOTE_ON_SIGNAL,
NOTE_OFF_SIGNAL,
+ REST_SIGNAL,
LAST_SIGNAL
};
static guint piano_keyboard_signals[LAST_SIGNAL] = { 0 };
static void
-draw_keyboard_cue(PianoKeyboard *pk)
+draw_keyboard_cue(PianoKeyboard *pk, cairo_t* cr)
{
int w = pk->notes[0].w;
int h = pk->notes[0].h;
- GdkGC *gc = GTK_WIDGET(pk)->style->fg_gc[0];
-
int first_note_in_lower_row = (pk->octave + 5) * 12;
int last_note_in_lower_row = (pk->octave + 6) * 12 - 1;
int first_note_in_higher_row = (pk->octave + 6) * 12;
int last_note_in_higher_row = (pk->octave + 7) * 12 + 4;
- gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_lower_row].x + 3,
- h - 6, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
+ cairo_set_source_rgb (cr, 1.0f, 0.0f, 0.0f);
+ cairo_move_to (cr, pk->notes[first_note_in_lower_row].x + 3, h - 6);
+ cairo_line_to (cr, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
+ cairo_stroke (cr);
- gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_higher_row].x + 3,
- h - 9, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
+ cairo_set_source_rgb (cr, 0.0f, 0.0f, 1.0f);
+ cairo_move_to (cr, pk->notes[first_note_in_higher_row].x + 3, h - 9);
+ cairo_line_to (cr, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
+ cairo_stroke (cr);
}
-static void
-draw_note(PianoKeyboard *pk, int note)
+static void
+queue_note_draw (PianoKeyboard* pk, int note)
{
- GdkColor black = {0, 0, 0, 0};
- GdkColor white = {0, 65535, 65535, 65535};
+ GdkWindow* w = GTK_WIDGET(pk)->window;
+
+ if (w) {
+ GdkRectangle r;
- GdkGC *gc = GTK_WIDGET(pk)->style->fg_gc[0];
- GtkWidget *widget;
+ r.x = pk->notes[note].x;
+ r.y = 0;
+ r.width = pk->notes[note].w;
+ r.height = pk->notes[note].h;
+ gdk_window_invalidate_rect (w, &r, TRUE);
+ }
+}
+
+static void
+draw_note(PianoKeyboard *pk, cairo_t* cr, int note)
+{
int is_white = pk->notes[note].white;
int x = pk->notes[note].x;
int w = pk->notes[note].w;
int h = pk->notes[note].h;
- if (pk->notes[note].pressed || pk->notes[note].sustained)
- is_white = !is_white;
+ if (pk->notes[note].pressed || pk->notes[note].sustained) {
+ if (is_white) {
+ cairo_set_source_rgb (cr, 0.60f, 0.60f, 0.60f);
+ } else {
+ cairo_set_source_rgb (cr, 0.50f, 0.50f, 0.50f);
+ }
+ } else {
+ if (is_white) {
+ cairo_set_source_rgb (cr, 1.0f, 1.0f, 1.0f);
+ } else {
+ cairo_set_source_rgb (cr, 0.0f, 0.0f, 0.0f);
+ }
+ }
+
+ cairo_set_line_width (cr, 1.0);
- if (is_white)
- gdk_gc_set_rgb_fg_color(gc, &white);
- else
- gdk_gc_set_rgb_fg_color(gc, &black);
+ cairo_rectangle (cr, x, 0, w, h);
+ cairo_fill (cr);
- gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, TRUE, x, 0, w, h);
- gdk_gc_set_rgb_fg_color(gc, &black);
- gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, FALSE, x, 0, w, h);
+ cairo_set_source_rgb(cr, 0.0f, 0.0f, 0.0f); /* black outline */
+ cairo_rectangle (cr, x, 0, w, h);
+ cairo_stroke (cr);
- if (pk->enable_keyboard_cue)
- draw_keyboard_cue(pk);
+ if (pk->enable_keyboard_cue) {
+ draw_keyboard_cue (pk, cr);
+ }
/* We need to redraw black keys that partially obscure the white one. */
- if (note < NNOTES - 2 && !pk->notes[note + 1].white)
- draw_note(pk, note + 1);
-
- if (note > 0 && !pk->notes[note - 1].white)
- draw_note(pk, note - 1);
+ if (note < NNOTES - 2 && !pk->notes[note + 1].white) {
+ draw_note(pk, cr, note + 1);
+ }
- /*
- * XXX: This doesn't really belong here. Originally I wanted to pack PianoKeyboard into GtkFrame
- * packed into GtkAlignment. I failed to make it behave the way I want. GtkFrame would need
- * to adapt to the "proper" size of PianoKeyboard, i.e. to the useful_width, not allocated width;
- * that didn't work.
- */
- widget = GTK_WIDGET(pk);
- gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, pk->widget_margin, 0,
- widget->allocation.width - pk->widget_margin * 2 + 1, widget->allocation.height);
+ if (note > 0 && !pk->notes[note - 1].white) {
+ draw_note(pk, cr, note - 1);
+ }
}
-static int
+static int
press_key(PianoKeyboard *pk, int key)
{
assert(key >= 0);
else
pk->notes[key].sustained = 0;
+ if (pk->monophonic && pk->last_key != key) {
+ pk->notes[pk->last_key].pressed = 0;
+ pk->notes[pk->last_key].sustained = 0;
+ queue_note_draw(pk, pk->last_key);
+ }
+ pk->last_key = key;
+
pk->notes[key].pressed = 1;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key);
- draw_note(pk, key);
+ queue_note_draw(pk, key);
return 1;
}
-static int
+static int
release_key(PianoKeyboard *pk, int key)
{
assert(key >= 0);
return 0;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", key);
- draw_note(pk, key);
+ queue_note_draw(pk, key);
return 1;
}
-static void
+static void
+rest (PianoKeyboard* pk)
+{
+ g_signal_emit_by_name(GTK_WIDGET(pk), "rest");
+}
+
+static void
stop_unsustained_notes(PianoKeyboard *pk)
{
int i;
if (pk->notes[i].pressed && !pk->notes[i].sustained) {
pk->notes[i].pressed = 0;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
- draw_note(pk, i);
+ queue_note_draw(pk, i);
}
}
}
-static void
+static void
stop_sustained_notes(PianoKeyboard *pk)
{
int i;
pk->notes[i].pressed = 0;
pk->notes[i].sustained = 0;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
- draw_note(pk, i);
+ queue_note_draw(pk, i);
}
}
}
{
assert(pk->key_bindings != NULL);
- g_hash_table_insert(pk->key_bindings, (gpointer)key, (gpointer)((intptr_t)note));
+ g_hash_table_insert(pk->key_bindings, (const gpointer)key, (gpointer)((intptr_t)note));
}
static void
g_hash_table_remove_all(pk->key_bindings);
}
-static void
+static void
bind_keys_qwerty(PianoKeyboard *pk)
{
clear_notes(pk);
+ bind_key(pk, "space", 128);
+
/* Lower keyboard row - "zxcvbnm". */
bind_key(pk, "z", 12); /* C0 */
bind_key(pk, "s", 13);
bind_key(pk, "p", 40);
}
-static void
+static void
bind_keys_qwertz(PianoKeyboard *pk)
{
bind_keys_qwerty(pk);
{
clear_notes(pk);
+ bind_key(pk, "space", 128);
+
/* Lower keyboard row - "wxcvbn,". */
bind_key(pk, "w", 12); /* C0 */
bind_key(pk, "s", 13);
bind_key(pk, "p", 40);
}
-static gint
-keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer notused)
+static gint
+keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
{
int note;
char *key;
GdkKeymapKey kk;
PianoKeyboard *pk = PIANO_KEYBOARD(mk);
+ (void) ignored;
+
/* We're not using event->keyval, because we need keyval with level set to 0.
E.g. if user holds Shift and presses '7', we want to get a '7', not '&'. */
kk.keycode = event->hardware_keycode;
return FALSE;
}
+ if (note == 128) {
+ if (event->type == GDK_KEY_RELEASE) {
+ rest (pk);
+ }
+
+ return TRUE;
+ }
+
note += pk->octave * 12;
assert(note >= 0);
return TRUE;
}
-static int
+static int
get_note_for_xy(PianoKeyboard *pk, int x, int y)
{
int height = GTK_WIDGET(pk)->allocation.height;
return -1;
}
-static gboolean
-mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer notused)
+static gboolean
+mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ignored)
{
int x = event->x;
int y = event->y;
int note = get_note_for_xy(pk, x, y);
+ (void) ignored;
+
if (event->button != 1)
return TRUE;
return TRUE;
}
-static gboolean
-mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer notused)
+static gboolean
+mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer ignored)
{
int note;
+ (void) ignored;
+
if ((event->state & GDK_BUTTON1_MASK) == 0)
return TRUE;
note = get_note_for_xy(pk, event->x, event->y);
if (note != pk->note_being_pressed_using_mouse && note >= 0) {
-
+
if (pk->note_being_pressed_using_mouse >= 0)
release_key(pk, pk->note_being_pressed_using_mouse);
press_key(pk, note);
{
int i;
PianoKeyboard *pk = PIANO_KEYBOARD(widget);
+ cairo_t* cr = gdk_cairo_create (GDK_DRAWABLE (GTK_WIDGET(pk)->window));
- for (i = 0; i < NNOTES; i++)
- draw_note(pk, i);
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ for (i = 0; i < NNOTES; i++) {
+ GdkRectangle r;
+
+ r.x = pk->notes[i].x;
+ r.y = 0;
+ r.width = pk->notes[i].w;
+ r.height = pk->notes[i].h;
+
+ switch (gdk_region_rect_in (event->region, &r)) {
+ case GDK_OVERLAP_RECTANGLE_PART:
+ case GDK_OVERLAP_RECTANGLE_IN:
+ draw_note (pk, cr, i);
+ break;
+ default:
+ break;
+ }
+ }
+
+ cairo_destroy (cr);
return TRUE;
}
-static void
-piano_keyboard_size_request(GtkWidget *widget, GtkRequisition *requisition)
+static void
+piano_keyboard_size_request(GtkWidget* w, GtkRequisition *requisition)
{
+ (void) w;
+
requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH;
requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
}
/* Set up signals. */
piano_keyboard_signals[NOTE_ON_SIGNAL] = g_signal_new ("note-on",
- G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
piano_keyboard_signals[NOTE_OFF_SIGNAL] = g_signal_new ("note-off",
- G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
- widget_klass = (GtkWidgetClass*) klass;
+ piano_keyboard_signals[REST_SIGNAL] = g_signal_new ("rest",
+ G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+ 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+ widget_klass = (GtkWidgetClass*) klass;
widget_klass->expose_event = piano_keyboard_expose;
widget_klass->size_request = piano_keyboard_size_request;
g_signal_connect(G_OBJECT(mk), "button-press-event", G_CALLBACK(mouse_button_event_handler), NULL);
g_signal_connect(G_OBJECT(mk), "button-release-event", G_CALLBACK(mouse_button_event_handler), NULL);
g_signal_connect(G_OBJECT(mk), "motion-notify-event", G_CALLBACK(mouse_motion_event_handler), NULL);
- g_signal_connect(G_OBJECT(mk), "key-press-event", G_CALLBACK(keyboard_event_handler), NULL);
- g_signal_connect(G_OBJECT(mk), "key-release-event", G_CALLBACK(keyboard_event_handler), NULL);
+ g_signal_connect(G_OBJECT(mk), "key-press-event", G_CALLBACK(keyboard_event_handler), NULL);
+ g_signal_connect(G_OBJECT(mk), "key-release-event", G_CALLBACK(keyboard_event_handler), NULL);
}
GType
sizeof (PianoKeyboard),
0, /* n_preallocs */
(GInstanceInitFunc) piano_keyboard_init,
+ 0, /* value_table */
};
- mk_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "PianoKeyboard", &mk_info, 0);
+ mk_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "PianoKeyboard", &mk_info, (GTypeFlags)0);
}
return mk_type;
GtkWidget *
piano_keyboard_new(void)
{
- GtkWidget *widget = gtk_type_new(piano_keyboard_get_type());
+ GtkWidget *widget = (GtkWidget*)gtk_type_new(piano_keyboard_get_type());
PianoKeyboard *pk = PIANO_KEYBOARD(widget);
pk->enable_keyboard_cue = 0;
pk->octave = 4;
pk->note_being_pressed_using_mouse = -1;
- memset((void *)pk->notes, 0, sizeof(struct Note) * NNOTES);
+ pk->last_key = 0;
+ pk->monophonic = FALSE;
+
+ memset((void *)pk->notes, 0, sizeof(struct PKNote) * NNOTES);
+
pk->key_bindings = g_hash_table_new(g_str_hash, g_str_equal);
bind_keys_qwerty(pk);
pk->enable_keyboard_cue = enabled;
}
+void
+piano_keyboard_set_monophonic(PianoKeyboard *pk, gboolean monophonic)
+{
+ pk->monophonic = monophonic;
+}
+
void
piano_keyboard_sustain_press(PianoKeyboard *pk)
{
}
}
-void
+void
piano_keyboard_sustain_release(PianoKeyboard *pk)
{
if (pk->maybe_stop_sustained_notes)
{
if (pk->notes[note].pressed == 0) {
pk->notes[note].pressed = 1;
- draw_note(pk, note);
+ queue_note_draw (pk, note);
}
}
if (pk->notes[note].pressed || pk->notes[note].sustained) {
pk->notes[note].pressed = 0;
pk->notes[note].sustained = 0;
- draw_note(pk, note);
+ queue_note_draw (pk, note);
}
}
{
assert(layout);
- if (!strcasecmp(layout, "QWERTY")) {
+ if (!g_ascii_strcasecmp(layout, "QWERTY")) {
bind_keys_qwerty(pk);
- } else if (!strcasecmp(layout, "QWERTZ")) {
+ } else if (!g_ascii_strcasecmp(layout, "QWERTZ")) {
bind_keys_qwertz(pk);
- } else if (!strcasecmp(layout, "AZERTY")) {
+ } else if (!g_ascii_strcasecmp(layout, "AZERTY")) {
bind_keys_azerty(pk);
} else {
return FALSE;
}
-