ti83-sdk/tool/tilem-src/gui/event.c

437 lines
11 KiB
C

/*
* TilEm II
*
* Copyright (c) 2010-2011 Thibault Duponchelle
* Copyright (c) 2010-2011 Benjamin Moody
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <ticalcs.h>
#include <tilem.h>
#include <scancodes.h>
#include "gui.h"
#include "files.h"
#include "filedlg.h"
/* Table for translating skin-file key number (based on actual
position, and defined by the VTI/TiEmu file formats) into a
scancode. Note that the TILEM_KEY_* constants are named according
to the TI-83 keypad layout; other models use different names for
the keys, but the same scancodes. */
static const int keycode_map[] =
{ TILEM_KEY_YEQU,
TILEM_KEY_WINDOW,
TILEM_KEY_ZOOM,
TILEM_KEY_TRACE,
TILEM_KEY_GRAPH,
TILEM_KEY_2ND,
TILEM_KEY_MODE,
TILEM_KEY_DEL,
TILEM_KEY_LEFT,
TILEM_KEY_RIGHT,
TILEM_KEY_UP,
TILEM_KEY_DOWN,
TILEM_KEY_ALPHA,
TILEM_KEY_GRAPHVAR,
TILEM_KEY_STAT,
TILEM_KEY_MATH,
TILEM_KEY_MATRIX,
TILEM_KEY_PRGM,
TILEM_KEY_VARS,
TILEM_KEY_CLEAR,
TILEM_KEY_RECIP,
TILEM_KEY_SIN,
TILEM_KEY_COS,
TILEM_KEY_TAN,
TILEM_KEY_POWER,
TILEM_KEY_SQUARE,
TILEM_KEY_COMMA,
TILEM_KEY_LPAREN,
TILEM_KEY_RPAREN,
TILEM_KEY_DIV,
TILEM_KEY_LOG,
TILEM_KEY_7,
TILEM_KEY_8,
TILEM_KEY_9,
TILEM_KEY_MUL,
TILEM_KEY_LN,
TILEM_KEY_4,
TILEM_KEY_5,
TILEM_KEY_6,
TILEM_KEY_SUB,
TILEM_KEY_STORE,
TILEM_KEY_1,
TILEM_KEY_2,
TILEM_KEY_3,
TILEM_KEY_ADD,
TILEM_KEY_ON,
TILEM_KEY_0,
TILEM_KEY_DECPNT,
TILEM_KEY_CHS,
TILEM_KEY_ENTER };
/* Find the keycode for the key (if any) at the given position. If
the keys overlap, choose the "nearest" (according to Manhattan
distance to the midpoint.) */
static int scan_click(const SKIN_INFOS* skin, double x, double y)
{
guint ix, iy, nearest = 0, i;
int dx, dy, d, best_d = G_MAXINT;
if (!skin)
return 0;
ix = (x * skin->sx + 0.5);
iy = (y * skin->sy + 0.5);
for (i = 0; i < G_N_ELEMENTS(keycode_map); i++) {
if (ix >= skin->keys_pos[i].left
&& ix < skin->keys_pos[i].right
&& iy >= skin->keys_pos[i].top
&& iy < skin->keys_pos[i].bottom) {
dx = (skin->keys_pos[i].left + skin->keys_pos[i].right
- 2 * ix);
dy = (skin->keys_pos[i].top + skin->keys_pos[i].bottom
- 2 * iy);
d = ABS(dx) + ABS(dy);
if (d < best_d) {
best_d = d;
nearest = keycode_map[i];
}
}
}
return nearest;
}
/* Retrieve pointer coordinates for an input device. */
static void get_device_pointer(GdkWindow *win, GdkDevice *dev,
gdouble *x, gdouble *y, GdkModifierType *mask)
{
gdouble *axes;
int i;
axes = g_new(gdouble, dev->num_axes);
gdk_device_get_state(dev, win, axes, mask);
for (i = 0; i < dev->num_axes; i++) {
if (x && dev->axes[i].use == GDK_AXIS_X)
*x = axes[i];
else if (y && dev->axes[i].use == GDK_AXIS_Y)
*y = axes[i];
}
g_free(axes);
}
/* Show a nice GtkAboutDialog */
void show_about()
{
GtkWidget *dialog = gtk_about_dialog_new();
gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "2.0");
gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "(c) Benjamin Moody\n(c) Thibault Duponchelle\n(c) Luc Bruant\n");
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "TilEm is a TI Linux Emulator.\n It emulates all current z80 models.\n TI73, TI76, TI81, TI82, TI83(+)(SE), TI84+(SE), TI85 and TI86 ;D");
gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://lpg.ticalc.org/prj_tilem/");
/* Add the logo */
char* tilem_ban = get_shared_file_path("pixs", "tilem_ban.png", NULL);
if(tilem_ban) {
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(tilem_ban, NULL);
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
g_object_unref(pixbuf), pixbuf = NULL;
}
gtk_dialog_run(GTK_DIALOG (dialog));
gtk_widget_destroy(dialog);
}
void launch_debugger(TilemEmulatorWindow *ewin)
{
if (!ewin->emu->dbg)
ewin->emu->dbg = tilem_debugger_new(ewin->emu);
tilem_debugger_show(ewin->emu->dbg);
}
/* Press a key, ensuring that at most one key is "pressed" at a time
due to this function (if pointer moves or is released, we don't
want the old key held down.)
FIXME: on multi-pointer displays, allow each input device to act
separately */
static void press_mouse_key(TilemEmulatorWindow* ewin, int key)
{
if (ewin->mouse_key == key)
return;
tilem_calc_emulator_release_key(ewin->emu, ewin->mouse_key);
tilem_calc_emulator_press_key(ewin->emu, key);
ewin->mouse_key = key;
}
/* Mouse button pressed */
gboolean mouse_press_event(G_GNUC_UNUSED GtkWidget* w, GdkEventButton *event,
gpointer data)
{
TilemEmulatorWindow* ewin = data;
int key;
key = scan_click(ewin->skin, event->x, event->y);
if (event->button == 1) {
/* button 1: press key until button is released or
pointer moves away */
press_mouse_key(ewin, key);
return TRUE;
}
else if (event->button == 2) {
/* button 2: hold key down permanently */
tilem_calc_emulator_press_key(ewin->emu, key);
return TRUE;
}
else if (event->button == 3) {
/* button 3: popup menu */
gtk_menu_popup(GTK_MENU(ewin->popup_menu),
NULL, NULL, NULL, NULL,
event->button, event->time);
return TRUE;
}
else
return FALSE;
}
/* Mouse pointer moved */
gboolean pointer_motion_event(G_GNUC_UNUSED GtkWidget* w, GdkEventMotion *event,
gpointer data)
{
TilemEmulatorWindow* ewin = data;
int key;
if (event->is_hint)
get_device_pointer(event->window, event->device,
&event->x, &event->y, &event->state);
if (event->state & GDK_BUTTON1_MASK)
key = scan_click(ewin->skin, event->x, event->y);
else
key = 0;
press_mouse_key(ewin, key);
return FALSE;
}
/* Mouse button released */
gboolean mouse_release_event(G_GNUC_UNUSED GtkWidget* w, GdkEventButton *event,
gpointer data)
{
TilemEmulatorWindow* ewin = data;
if (event->button == 1)
press_mouse_key(ewin, 0);
return FALSE;
}
/* Find key binding for the given keysym and modifiers */
static TilemKeyBinding* find_key_binding_for_keysym(TilemCalcEmulator* emu,
guint keyval,
GdkModifierType mods)
{
int i;
for (i = 0; i < emu->nkeybindings; i++)
if (keyval == emu->keybindings[i].keysym
&& mods == emu->keybindings[i].modifiers)
return &emu->keybindings[i];
return NULL;
}
/* Find key binding matching the given event */
static TilemKeyBinding* find_key_binding(TilemCalcEmulator* emu,
GdkEventKey* event)
{
GdkDisplay *dpy;
GdkKeymap *km;
guint keyval;
GdkModifierType consumed, mods;
TilemKeyBinding *kb;
dpy = gdk_drawable_get_display(event->window);
km = gdk_keymap_get_for_display(dpy);
/* determine the relevant set of modifiers */
gdk_keymap_translate_keyboard_state(km, event->hardware_keycode,
event->state, event->group,
&keyval, NULL, NULL, &consumed);
mods = (event->state & ~consumed
& (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK));
if (event->state & GDK_LOCK_MASK
&& (kb = find_key_binding_for_keysym(emu, keyval,
mods | GDK_LOCK_MASK))) {
return kb;
}
return find_key_binding_for_keysym(emu, keyval, mods);
}
/* Key-press event */
gboolean key_press_event(G_GNUC_UNUSED GtkWidget* w, GdkEventKey* event,
gpointer data)
{
TilemEmulatorWindow *ewin = data;
TilemKeyBinding *kb;
int i, key;
unsigned int hwkey;
/* Ignore repeating keys */
for (i = 0; i < 64; i++)
if (ewin->keypress_keycodes[i] == event->hardware_keycode)
return TRUE;
if (ewin->sequence_keycode == event->hardware_keycode)
return TRUE;
if (!(kb = find_key_binding(ewin->emu, event)))
return FALSE;
hwkey = event->hardware_keycode;
if (kb->nscancodes == 1) {
/* if queue is empty, just press the key; otherwise
add it to the queue */
key = kb->scancodes[0];
if (tilem_calc_emulator_press_or_queue(ewin->emu, key))
ewin->sequence_keycode = hwkey;
else
ewin->keypress_keycodes[key] = hwkey;
}
else {
tilem_calc_emulator_queue_keys(ewin->emu, kb->scancodes,
kb->nscancodes);
ewin->sequence_keycode = hwkey;
}
return TRUE;
}
/* Key-release event */
gboolean key_release_event(G_GNUC_UNUSED GtkWidget* w, GdkEventKey* event,
gpointer data)
{
TilemEmulatorWindow *ewin = data;
int i;
/* Check if the key that was just released was one that
activated a calculator keypress. (Do not try to look up
event->keyval; modifiers may have changed since the key was
pressed.) */
for (i = 0; i < 64; i++) {
if (ewin->keypress_keycodes[i] == event->hardware_keycode) {
tilem_calc_emulator_release_key(ewin->emu, i);
ewin->keypress_keycodes[i] = 0;
}
}
if (ewin->sequence_keycode == event->hardware_keycode) {
tilem_calc_emulator_release_queued_key(ewin->emu);
ewin->sequence_keycode = 0;
}
return FALSE;
}
static void place_menu(GtkMenu *menu, gint *x, gint *y,
gboolean *push_in, gpointer data)
{
GtkWidget *w = data;
GdkWindow *win;
GdkScreen *screen;
int n;
win = gtk_widget_get_window(w);
gdk_window_get_origin(win, x, y);
screen = gdk_drawable_get_screen(win);
n = gdk_screen_get_monitor_at_point(screen, *x, *y);
gtk_menu_set_monitor(menu, n);
*push_in = FALSE;
}
/* Pop up menu on main window */
gboolean popup_menu_event(GtkWidget* w, gpointer data)
{
TilemEmulatorWindow *ewin = data;
gtk_menu_popup(GTK_MENU(ewin->popup_menu),
NULL, NULL, &place_menu, w,
0, gtk_get_current_event_time());
return TRUE;
}
/* Callback function for the drag and drop event */
void drag_data_received(G_GNUC_UNUSED GtkWidget *win,
G_GNUC_UNUSED GdkDragContext *dc,
G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
GtkSelectionData *seldata,
G_GNUC_UNUSED guint info, G_GNUC_UNUSED guint t,
gpointer data)
{
TilemEmulatorWindow *ewin = data;
gchar **uris, **filenames;
gint i, j, n;
uris = gtk_selection_data_get_uris(seldata);
if (!uris)
return;
n = g_strv_length(uris);
filenames = g_new0(gchar *, n + 1);
for (i = j = 0; i < n; i++) {
filenames[j] = g_filename_from_uri(uris[i], NULL, NULL);
if (filenames[j])
j++;
}
filenames[j] = NULL;
load_files(ewin, filenames);
g_strfreev(filenames);
}