1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2011 Benjamin Moody
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <gtk/gtk.h>
27 #include <ticalcs.h>
28 #include <tilem.h>
29 #include <scancodes.h>
30 
31 #include "gui.h"
32 
33 #define NGROUPS 7
34 #define NKEYS 8
35 
36 /* Check-button toggled */
group_toggled(GtkToggleButton * btn,gpointer data)37 static void group_toggled(GtkToggleButton *btn, gpointer data)
38 {
39 	TilemKeypadDialog *kpdlg = data;
40 	TilemCalcEmulator *emu;
41 	int i;
42 	gboolean state;
43 
44 	if (kpdlg->refreshing)
45 		return;
46 
47 	g_return_if_fail(kpdlg->dbg != NULL);
48 	g_return_if_fail(kpdlg->dbg->emu != NULL);
49 	emu = kpdlg->dbg->emu;
50 
51 	state = gtk_toggle_button_get_active(btn);
52 
53 	for (i = 0; i < NGROUPS; i++) {
54 		if (GTK_WIDGET(btn) == kpdlg->output[i]) {
55 			tilem_calc_emulator_lock(emu);
56 			if (state)
57 				emu->calc->keypad.group &= ~(1 << i);
58 			else
59 				emu->calc->keypad.group |= (1 << i);
60 			tilem_calc_emulator_unlock(emu);
61 
62 			tilem_keypad_dialog_refresh(kpdlg);
63 			return;
64 		}
65 	}
66 
67 	g_return_if_reached();
68 }
69 
70 /* Key toggled */
key_toggled(GtkToggleButton * btn,gpointer data)71 static void key_toggled(GtkToggleButton *btn, gpointer data)
72 {
73 	TilemKeypadDialog *kpdlg = data;
74 	TilemCalcEmulator *emu;
75 	int i, j, k;
76 	gboolean state;
77 
78 	if (kpdlg->refreshing)
79 		return;
80 
81 	g_return_if_fail(kpdlg->dbg != NULL);
82 	g_return_if_fail(kpdlg->dbg->emu != NULL);
83 	emu = kpdlg->dbg->emu;
84 
85 	state = gtk_toggle_button_get_active(btn);
86 
87 	for (i = 0; i < NGROUPS; i++) {
88 		for (j = 0; j < NKEYS; j++) {
89 			if (GTK_WIDGET(btn) == kpdlg->keys[i][j]) {
90 				k = i * 8 + j + 1;
91 				if (state)
92 					tilem_calc_emulator_press_key(emu, k);
93 				else
94 					tilem_calc_emulator_release_key(emu, k);
95 				return;
96 			}
97 		}
98 	}
99 
100 	g_return_if_reached();
101 }
102 
103 /* Create a new TilemKeypadDialog. */
tilem_keypad_dialog_new(TilemDebugger * dbg)104 TilemKeypadDialog *tilem_keypad_dialog_new(TilemDebugger *dbg)
105 {
106 	TilemKeypadDialog *kpdlg;
107 	GtkWidget *tbl1, *tbl2, *hbox, *vbox, *btn, *lbl;
108 	int i, j;
109 	char buf[20];
110 
111 	g_return_val_if_fail(dbg != NULL, NULL);
112 
113 	kpdlg = g_slice_new0(TilemKeypadDialog);
114 	kpdlg->dbg = dbg;
115 
116 	kpdlg->window = gtk_dialog_new_with_buttons
117 		("Keypad", NULL, 0,
118 		 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
119 		 NULL);
120 
121 	g_signal_connect(kpdlg->window, "delete-event",
122 	                 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
123 
124 	tbl1 = gtk_table_new(NGROUPS, NKEYS, TRUE);
125 	hbox = gtk_hbox_new(TRUE, 0);
126 	vbox = gtk_vbox_new(TRUE, 0);
127 
128 	/* Keypad buttons (labels will be filled in, and buttons
129 	   shown/hidden, by tilem_keypad_dialog_calc_changed())
130 
131 	   Buttons are displayed right to left, top to bottom; this
132 	   way, the layout of groups 1-5 roughly corresponds to the
133 	   physical layout of the keys, and the "input" value can be
134 	   read across the bottom as a binary number. */
135 
136 	for (i = 0; i < NGROUPS; i++) {
137 		for (j = 0; j < NKEYS; j++) {
138 			btn = gtk_toggle_button_new_with_label("");
139 			kpdlg->keys[i][j] = btn;
140 			gtk_table_attach(GTK_TABLE(tbl1), btn,
141 			                 NKEYS - j - 1, NKEYS - j,
142 			                 i, i + 1,
143 			                 GTK_FILL, GTK_FILL, 2, 2);
144 
145 			g_signal_connect(btn, "toggled",
146 			                 G_CALLBACK(key_toggled), kpdlg);
147 
148 			gtk_widget_set_no_show_all(btn, TRUE);
149 		}
150 	}
151 
152 	/* Check buttons for key groups (output bits) */
153 
154 	for (i = 0; i < NGROUPS; i++) {
155 		g_snprintf(buf, sizeof(buf), "Group %d", i);
156 		btn = gtk_check_button_new_with_label(buf);
157 		kpdlg->output[i] = btn;
158 		gtk_box_pack_start(GTK_BOX(vbox), btn, FALSE, TRUE, 2);
159 
160 		g_signal_connect(btn, "toggled",
161 		                 G_CALLBACK(group_toggled), kpdlg);
162 	}
163 
164 	/* Labels for input bits */
165 
166 	for (j = NKEYS - 1; j >= 0; j--) {
167 		kpdlg->input[j] = lbl = gtk_label_new("");
168 		gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, TRUE, 2);
169 	}
170 
171 	tbl2 = gtk_table_new(3, 2, FALSE);
172 	gtk_container_set_border_width(GTK_CONTAINER(tbl2), 6);
173 	gtk_table_set_row_spacings(GTK_TABLE(tbl2), 12);
174 	gtk_table_set_col_spacings(GTK_TABLE(tbl2), 12);
175 
176 	lbl = gtk_label_new(NULL);
177 	gtk_label_set_markup(GTK_LABEL(lbl), "<b>Scan Groups</b>");
178 	gtk_table_attach(GTK_TABLE(tbl2), lbl, 0, 1, 0, 1,
179 	                 GTK_FILL, GTK_FILL, 0, 0);
180 
181 	lbl = gtk_label_new(NULL);
182 	gtk_label_set_markup(GTK_LABEL(lbl), "<b>Keys</b>");
183 	gtk_table_attach(GTK_TABLE(tbl2), lbl, 1, 2, 0, 1,
184 	                 GTK_FILL, GTK_FILL, 0, 0);
185 
186 	lbl = gtk_label_new("Input Value:");
187 	gtk_table_attach(GTK_TABLE(tbl2), lbl, 0, 1, 2, 3,
188 	                 GTK_FILL, GTK_FILL, 0, 0);
189 
190 	gtk_table_attach(GTK_TABLE(tbl2), vbox, 0, 1, 1, 2,
191 	                 GTK_FILL, GTK_FILL, 0, 0);
192 	gtk_table_attach(GTK_TABLE(tbl2), tbl1, 1, 2, 1, 2,
193 	                 GTK_FILL, GTK_FILL, 0, 0);
194 	gtk_table_attach(GTK_TABLE(tbl2), hbox, 1, 2, 2, 3,
195 	                 GTK_FILL, GTK_FILL, 0, 0);
196 
197 	gtk_widget_show_all(tbl2);
198 
199 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(kpdlg->window));
200 	gtk_box_pack_start(GTK_BOX(vbox), tbl2, FALSE, FALSE, 0);
201 
202 	tilem_keypad_dialog_calc_changed(kpdlg);
203 
204 	return kpdlg;
205 }
206 
207 /* Free a TilemKeypadDialog. */
tilem_keypad_dialog_free(TilemKeypadDialog * kpdlg)208 void tilem_keypad_dialog_free(TilemKeypadDialog *kpdlg)
209 {
210 	g_return_if_fail(kpdlg != NULL);
211 	if (kpdlg->window)
212 		gtk_widget_destroy(kpdlg->window);
213 	g_slice_free(TilemKeypadDialog, kpdlg);
214 }
215 
216 /* New calculator loaded. */
tilem_keypad_dialog_calc_changed(TilemKeypadDialog * kpdlg)217 void tilem_keypad_dialog_calc_changed(TilemKeypadDialog *kpdlg)
218 {
219 	TilemCalc *calc;
220 	int i, j, k;
221 	GtkWidget *btn, *lbl;
222 
223 	g_return_if_fail(kpdlg != NULL);
224 	g_return_if_fail(kpdlg->dbg != NULL);
225 	g_return_if_fail(kpdlg->dbg->emu != NULL);
226 	g_return_if_fail(kpdlg->dbg->emu->calc != NULL);
227 	calc = kpdlg->dbg->emu->calc;
228 
229 	for (i = 0; i < NGROUPS; i++) {
230 		for (j = 0; j < NKEYS; j++) {
231 			btn = kpdlg->keys[i][j];
232 			k = i * 8 + j + 1;
233 			if (k != TILEM_KEY_ON
234 			    && calc->hw.keynames[k - 1] != NULL) {
235 				lbl = gtk_bin_get_child(GTK_BIN(btn));
236 				gtk_label_set_text(GTK_LABEL(lbl),
237 				                   calc->hw.keynames[k - 1]);
238 				gtk_widget_show(btn);
239 			}
240 			else {
241 				gtk_widget_hide(btn);
242 			}
243 		}
244 	}
245 
246 	tilem_keypad_dialog_refresh(kpdlg);
247 }
248 
249 /* Refresh key states. */
tilem_keypad_dialog_refresh(TilemKeypadDialog * kpdlg)250 void tilem_keypad_dialog_refresh(TilemKeypadDialog *kpdlg)
251 {
252 	int i, j;
253 	byte keys[NGROUPS], inval, outval;
254 	TilemCalcEmulator *emu;
255 	GtkWidget *btn, *lbl;
256 
257 	g_return_if_fail(kpdlg != NULL);
258 	g_return_if_fail(kpdlg->dbg != NULL);
259 	g_return_if_fail(kpdlg->dbg->emu != NULL);
260 	emu = kpdlg->dbg->emu;
261 
262 	if (kpdlg->refreshing)
263 		return;
264 
265 	kpdlg->refreshing = TRUE;
266 
267 	tilem_calc_emulator_lock(emu);
268 	for (i = 0; i < NGROUPS; i++)
269 		keys[i] = emu->calc->keypad.keysdown[i];
270 	outval = emu->calc->keypad.group;
271 	inval = tilem_keypad_read_keys(emu->calc);
272 	tilem_calc_emulator_unlock(emu);
273 
274 	for (i = 0; i < NGROUPS; i++) {
275 		for (j = 0; j < NKEYS; j++) {
276 			btn = kpdlg->keys[i][j];
277 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn),
278 			                             (keys[i] & (1 << j)));
279 		}
280 	}
281 
282 
283 	for (i = 0; i < NGROUPS; i++) {
284 		btn = kpdlg->output[i];
285 		if (emu->paused) {
286 			gtk_widget_set_sensitive(btn, TRUE);
287 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn),
288 			                             !(outval & (1 << i)));
289 		}
290 		else {
291 			gtk_widget_set_sensitive(btn, FALSE);
292 		}
293 	}
294 
295 	for (j = 0; j < NKEYS; j++) {
296 		lbl = kpdlg->input[j];
297 		gtk_label_set_text(GTK_LABEL(lbl),
298 		                   (emu->paused
299 		                    ? (inval & (1 << j) ? "1" : "0")
300 		                    : ""));
301 
302 	}
303 
304 	kpdlg->refreshing = FALSE;
305 }
306