#define SHIFT_R_KEYCODE 62
/* these can be increased to access more keys */
-#define MAX_ROWS 5
+#define MAX_ROWS 6 /* The last row only holds Quit and ShiftLatch, special-purpose keys */
#define MAX_COLUMNS 14
static AccessibleKeystrokeListener *key_listener;
static boolean caps_lock = False;
static GtkButton **buttons[MAX_ROWS];
+typedef enum {
+ SCAN_IDLE,
+ SCAN_LINES,
+ SCAN_LINES_DONE,
+ SCAN_KEYS,
+ SCAN_KEYS_DONE
+} ScanTimerState;
+
+typedef struct {
+ ScanTimerState timer_state;
+ gint scan_row;
+ gint scan_column;
+} ScanState;
+
+GdkColor *
+button_default_bg_color (GtkButton *button)
+{
+ static GdkColor bg_color;
+ static gboolean initialized = FALSE;
+ if (!initialized)
+ {
+ bg_color = gtk_widget_get_style (GTK_WIDGET (button))->bg[GTK_STATE_NORMAL];
+ initialized = TRUE;
+ }
+ return &bg_color;
+}
+
+GdkColor *
+button_default_selected_color (GtkButton *button)
+{
+ static GdkColor selected_color;
+ static gboolean initialized = FALSE;
+ if (!initialized)
+ {
+ selected_color = gtk_widget_get_style (GTK_WIDGET (button))->bg[GTK_STATE_SELECTED];
+ initialized = TRUE;
+ }
+ return &selected_color;
+}
+
+void
+select_key (gint lineno, gint keyno)
+{
+ /*
+ * Before we do this, we need to make sure we've saved the default normal bg.
+ * The button_default_bg_color() call caches this for us (as a side-effect).
+ * Probably we should do this a cleaner way...
+ */
+ button_default_bg_color (buttons [lineno][keyno]);
+ gtk_widget_modify_bg (GTK_WIDGET (buttons [lineno][keyno]),
+ GTK_STATE_NORMAL,
+ button_default_selected_color (buttons [lineno][keyno]));
+}
+
+void
+deselect_key (gint lineno, gint keyno)
+{
+ gtk_widget_modify_bg (GTK_WIDGET (buttons [lineno][keyno]),
+ GTK_STATE_NORMAL,
+ button_default_bg_color (buttons [lineno][keyno]));
+}
+
+void
+deselect_line (gint lineno)
+{
+ int i;
+ int max_columns = MAX_COLUMNS;
+ if (lineno == MAX_ROWS-1) max_columns = 2;
+ for (i=0; i<max_columns; ++i)
+ deselect_key (lineno, i);
+}
+
+void
+select_line (gint lineno)
+{
+ int i;
+ int max_columns = MAX_COLUMNS;
+ if (lineno == MAX_ROWS-1) max_columns = 2;
+ for (i=0; i<max_columns; ++i)
+ select_key (lineno, i);
+}
+
+
+static ScanState*
+scan_state ()
+{
+ static ScanState state = {SCAN_IDLE, 0, 0};
+ return &state;
+}
+
+static gboolean
+timeout_scan (gpointer data)
+{
+ ScanState *state = (ScanState *) data;
+ state->timer_state = SCAN_IDLE;
+ deselect_key (state->scan_row, state->scan_column);
+ return FALSE;
+}
+
+static gboolean
+increment_scan (gpointer data)
+{
+ ScanState *state = (ScanState *) data;
+ int max_columns;
+ switch (state->timer_state)
+ {
+ case SCAN_IDLE:
+/* happens if switch-break occurs before the timer fires, after SCAN_KEYS_DONE*/
+ return FALSE;
+ case SCAN_LINES:
+ deselect_line (state->scan_row);
+ state->scan_row = (++state->scan_row < MAX_ROWS) ? state->scan_row : 0;
+ select_line (state->scan_row);
+ g_print ("line %d\n", state->scan_row);
+ break;
+ case SCAN_KEYS:
+ deselect_key (state->scan_row, state->scan_column);
+ if (state->scan_row == MAX_ROWS-1) max_columns = 2;
+ else max_columns = MAX_COLUMNS; /* last row has only two keys */
+ state->scan_column = (++state->scan_column < max_columns) ? state->scan_column : 0;
+ select_key (state->scan_row, state->scan_column);
+ g_print ("row %d\n", state->scan_column);
+ break;
+ case SCAN_LINES_DONE:
+ case SCAN_KEYS_DONE:
+ return FALSE;
+ default:
+ }
+ return TRUE;
+}
+
+static void
+scan_start (unsigned int timestamp)
+{
+ ScanState *state = scan_state();
+ switch (state->timer_state)
+ {
+ case SCAN_IDLE:
+ state->timer_state = SCAN_LINES;
+ state->scan_column = 0;
+ state->scan_row = 0;
+ g_timeout_add_full (G_PRIORITY_HIGH_IDLE, 600, increment_scan, state, NULL);
+ select_line (state->scan_row);
+ break;
+ case SCAN_LINES_DONE:
+ state->timer_state = SCAN_KEYS;
+ g_timeout_add_full (G_PRIORITY_HIGH_IDLE, 600, increment_scan, state, NULL);
+ deselect_line (state->scan_row);
+ select_key (state->scan_row, state->scan_column);
+ break;
+ case SCAN_KEYS_DONE:
+ gtk_button_clicked (buttons[state->scan_row][state->scan_column]);
+ deselect_key (state->scan_row, state->scan_column);
+ state->timer_state = SCAN_IDLE;
+ break;
+ default:
+ g_print("unexpected state for 'scan start'\n");
+ }
+}
+
+static void
+scan_stop (unsigned int timestamp)
+{
+ /* find the element correspondin to this event's timestamp */
+ ScanState *state = scan_state();
+ switch (state->timer_state)
+ {
+ case SCAN_LINES:
+ state->timer_state = SCAN_LINES_DONE;
+ break;
+ case SCAN_KEYS:
+ state->timer_state = SCAN_KEYS_DONE;
+ g_timeout_add_full (G_PRIORITY_HIGH_IDLE, 1200, timeout_scan, state, NULL);
+ break;
+ case SCAN_IDLE:
+ break;
+ default:
+ g_print("unexpected state for 'scan stop'\n");
+ }
+}
+
static void
label_buttons(boolean shifted)
{
char *button_label;
char *keysymstring;
- for (i=0; i<MAX_ROWS; ++i)
+ for (i=0; i<MAX_ROWS-1; ++i) /* last row doesn't change */
{
for (j=0; j<MAX_COLUMNS; ++j, ++keycode)
{
}
static void
-do_shift (GtkButton *button)
-{
- static KeyCode shift_keycode = 0;
- if (!shift_keycode) shift_keycode = XKeysymToKeycode(GDK_DISPLAY(), (KeySym) 0xFFE1);
- /* Note: in a real onscreen keyboard shift keycode should not be hard-coded! */
+toggle_shift_latch (GtkButton *button)
+{
shift_latched = !shift_latched;
- generateKeyEvent (shift_keycode, shift_latched ? SPI_KEY_PRESS : SPI_KEY_RELEASE);
if (buttons) label_buttons (caps_lock || shift_latched);
}
static void
keysynth_exit()
{
- if (shift_latched) do_shift (NULL);
deregisterAccessibleKeystrokeListener (key_listener, SPI_KEYMASK_ALT );
deregisterAccessibleKeystrokeListener (switch_listener, SPI_KEYMASK_UNMODIFIED );
SPI_exit ();
static boolean
switch_callback (void *p)
{
+ AccessibleKeyStroke *key = (AccessibleKeyStroke *)p;
+ static is_down = FALSE;
+ if (key->type == Accessibility_KEY_RELEASED)
+ {
+ g_print ("spacebar up\n");
+ is_down = FALSE;
+ scan_stop (key->timestamp);
+ }
+ else
+ if (!is_down)
+ {
+ g_print ("spacebar down\n");
+ is_down = TRUE;
+ scan_start (key->timestamp);
+ }
+ /* catch the first, ignore the rest (will repeat) until keyrelease */
return FALSE;
}
static void
synth_keycode (GtkButton *button, KeyCode *keycode)
{
- if (shift_latched) do_shift (button);
+ static KeyCode shift_keycode = 0;
+ if (!shift_keycode) shift_keycode = XKeysymToKeycode(GDK_DISPLAY(), (KeySym) 0xFFE1);
+ /* Note: in a real onscreen keyboard shift keycode should not be hard-coded! */
if (*keycode)
{
if (*keycode == CAPSLOCK_KEYCODE)
caps_lock = !caps_lock;
label_buttons (caps_lock || shift_latched);
}
+ if (shift_latched)
+ generateKeyEvent (shift_keycode, SPI_KEY_PRESS);
+
generateKeyEvent ((long) *keycode, SPI_KEY_PRESSRELEASE);
- }
+
+ if (shift_latched)
+ {
+ generateKeyEvent (shift_keycode, SPI_KEY_RELEASE);
+ toggle_shift_latch (button);
+ }
+ }
}
static void
"GtkWidget::parent", window,
"GtkWidget::visible", TRUE,
NULL);
- for (i=0; i<MAX_ROWS; ++i)
+ for (i=0; i<MAX_ROWS-1; ++i)
{
hbox = gtk_widget_new (gtk_hbox_get_type(),
"GtkWidget::parent", container,
"GtkWidget::parent", container,
"GtkWidget::visible", TRUE,
NULL);
- button = g_object_connect (gtk_widget_new (gtk_button_get_type (),
- "GtkButton::label", "Quit",
- "GtkWidget::parent", hbox,
- "GtkWidget::visible", TRUE,
- NULL),
- "signal::clicked",
- button_exit, NULL,
- NULL);
- button = g_object_connect (gtk_widget_new (gtk_button_get_type (),
- "GtkButton::label", "ShiftLatch",
- "GtkWidget::parent", hbox,
- "GtkWidget::visible", TRUE,
- NULL),
- "signal::clicked",
- do_shift, NULL,
- NULL);
+ buttons[i] = g_new0 (GtkButton*, 2);
+ buttons[i][0] = g_object_connect (gtk_widget_new (gtk_button_get_type (),
+ "GtkButton::label", "Quit",
+ "GtkWidget::parent", hbox,
+ "GtkWidget::visible", TRUE,
+ NULL),
+ "signal::clicked",
+ button_exit, NULL,
+ NULL);
+ buttons[i][1] = g_object_connect (gtk_widget_new (gtk_button_get_type (),
+ "GtkButton::label", "ShiftLatch",
+ "GtkWidget::parent", hbox,
+ "GtkWidget::visible", TRUE,
+ NULL),
+ "signal::clicked",
+ toggle_shift_latch, NULL,
+ NULL);
label_buttons (caps_lock || shift_latched);
gtk_widget_show (window);
}
int
main(int argc, char **argv)
{
- AccessibleKeySet spacebar_set;
+ AccessibleKeySet switch_set;
if ((argc > 1) && (!strncmp(argv[1],"-h",2)))
{
SPI_KEYLISTENER_CANCONSUME | SPI_KEYLISTENER_ALL_WINDOWS);
create_vkbd();
- /* register a listener on the spacebar, to serve as a 'single switch' */
- spacebar_set.keysyms = g_new0 (unsigned long, 1);
- spacebar_set.keycodes = g_new0 (unsigned short, 1);
- spacebar_set.len = 1;
- spacebar_set.keysyms[0] = (unsigned long) ' ';
- spacebar_set.keycodes[0] = (unsigned short) 0;
+ /*
+ * Register a listener on an 'unused' key, to serve as a 'single switch'.
+ * On most Intel boxes there is at least one 'special' system key that does not
+ * have a non-zero keycode assigned in the Xserver, so we will intercept any keycode
+ * that is 'zero'. Often these the "windows" key or the "menu" key.
+ */
+ switch_set.keysyms = g_new0 (unsigned long, 1);
+ switch_set.keycodes = g_new0 (unsigned short, 1);
+ switch_set.len = 1;
+ switch_set.keysyms[0] = (unsigned long) 0;
+ switch_set.keycodes[0] = (unsigned short) 0;
switch_listener = createAccessibleKeystrokeListener(switch_callback);
- /* registerKeystrokeListener(switch_listener,
- &spacebar_set,
- SPI_KEYMASK_UNMODIFIED,
- (unsigned long) ( KeyPress | KeyRelease),
- SPI_KEYLISTENER_CANCONSUME);
- */
+ registerAccessibleKeystrokeListener(switch_listener,
+ &switch_set,
+ SPI_KEYMASK_UNMODIFIED,
+ (unsigned long) ( KeyPress | KeyRelease),
+ SPI_KEYLISTENER_CANCONSUME);
SPI_event_main(TRUE);
}
#define MSG_LEN 80
+static GtkWidget *window; /* TODO: clean up, with accessor func? */
+
+static void
+magnifier_realize (GtkWidget *widget)
+{
+ XWMHints wm_hints;
+ Atom wm_window_protocols[2];
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ wm_window_protocols[0] = gdk_x11_get_xatom_by_name ("WM_DELETE_WINDOW");
+ wm_window_protocols[1] = gdk_x11_get_xatom_by_name ("_NET_WM_PING");
+ }
+
+ wm_hints.flags = InputHint;
+ wm_hints.input = False;
+
+ XSetWMHints (GDK_WINDOW_XDISPLAY (widget->window),
+ GDK_WINDOW_XWINDOW (widget->window), &wm_hints);
+
+ XSetWMProtocols (GDK_WINDOW_XDISPLAY (widget->window),
+ GDK_WINDOW_XWINDOW (widget->window), wm_window_protocols, 2);
+}
+
+static void
+magnifier_exit()
+{
+ gtk_exit(0);
+}
+
static gboolean get_commands(GIOChannel* client,
GIOCondition condition,
gpointer data){
}
int main (int argc, char** argv){
- GtkWidget *window;
GIOChannel *mag_channel;
char *dpyname;
char env_string[ENV_STRING_MAX_SIZE];
}
gtk_init (&argc, &argv);
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ window = g_object_connect (gtk_widget_new (gtk_window_get_type (),
+ "user_data", NULL,
+ "can_focus", FALSE,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "title", "magnifier",
+ "allow_grow", FALSE,
+ "allow_shrink", FALSE,
+ "border_width", 10,
+ NULL),
+ "signal::realize", magnifier_realize, NULL,
+ "signal::destroy", magnifier_exit, NULL,
+ NULL);
+
drawing_area = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER (window),drawing_area);
gtk_widget_add_events(GTK_WIDGET(drawing_area),GDK_BUTTON_PRESS_MASK);
if (global_options.horizontal_split)
magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display,screen_num)/2;
else magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display, screen_num);
+ gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
gtk_widget_show_all (window);
gdk_window_move(window->window,
gdk_window_resize (window->window, magnifier->mag_data->mag_width, magnifier->mag_data->mag_height);
magnifier->mag_data->output_window = window;
if (global_options.fullscreen) gdk_window_stick (window->window);
- gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
gdk_window_set_functions(window->window, 0);
gdk_window_raise(window->window);
impl_magnifier_fullscreen (PortableServer_Servant servant,
CORBA_Environment *ev)
{
+ Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
+ magnifier->mag_data->mag_width = DisplayWidth (magnifier->mag_data->target_display, screen_num);
+ magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display, screen_num);
}
static void
CORBA_long y2,
CORBA_Environment *ev)
{
+ Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
+ magnifier->mag_data->mag_width = x2 - x1;
+ magnifier->mag_data->mag_height = y2 - y1;
+ gdk_window_move(window->window, x1, y1);
}
static void
CORBA_Environment *ev)
{
Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
- magnifier->mag_data->factor_x = (float) mag_factor_x;
- magnifier->mag_data->factor_y = (float) mag_factor_y;
+ if (zoom_region == (CORBA_short) 0) /* TODO: fix for multi-zoom-regions */
+ {
+ magnifier->mag_data->factor_x = (float) mag_factor_x;
+ magnifier->mag_data->factor_y = (float) mag_factor_y;
+ }
}
static void
CORBA_Environment *ev)
{
Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
+ /* TODO: implement */
}
static void
CORBA_Environment *ev)
{
Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
+ /* TODO: implement */
}
static CORBA_short
}
static CORBA_boolean
-impl_magnifier_get_zoom_region_params (PortableServer_Servant _servant,
+impl_magnifier_get_zoom_region_params (PortableServer_Servant servant,
const CORBA_short zoom_region,
CORBA_float * zx,
CORBA_float * zy, CORBA_long * x1,
CORBA_long * y2,
CORBA_Environment * ev)
{
- return CORBA_FALSE;
+ Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
+ if (zoom_region == (CORBA_short) 0)
+ {
+ *zx = magnifier->mag_data->factor_x;
+ *zy = magnifier->mag_data->factor_y;
+ *x1 = 0;
+ *y1 = 0;
+ *x2 = *x1 + magnifier->mag_data->mag_width;
+ *y2 = *y1 + magnifier->mag_data->mag_height;
+ return CORBA_TRUE;
+ }
+ else return CORBA_FALSE;
}
static void