1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "kc/system.h"
21
22 #include "kc/kc.h"
23 #include "kc/prefs/prefs.h"
24
25 #include "sys/sysdep.h"
26
27 #undef Status
28 #include "ui/status.h"
29
30 #include "ui/gtk/cmd.h"
31 #include "ui/gtk/main.h"
32
33 #include "libdbg/dbg.h"
34
35 class CMD_toggle_main : public CMD {
36 private:
37 MainWindow *_w;
38
39 public:
CMD_toggle_main(MainWindow * w)40 CMD_toggle_main(MainWindow *w) : CMD("ui-toggle-main") {
41 _w = w;
42 register_cmd("ui-status-bar-toggle", 0);
43 register_cmd("ui-menu-bar-toggle", 1);
44 }
45
execute(CMD_Args * args,CMD_Context context)46 void execute(CMD_Args *args, CMD_Context context) {
47 switch (context) {
48 case 0:
49 _w->status_bar_toggle();
50 break;
51 case 1:
52 _w->menu_bar_toggle();
53 break;
54 }
55 }
56 };
57
MainWindow(const char * ui_xml_file)58 MainWindow::MainWindow(const char *ui_xml_file) : UI_Gtk_Window(ui_xml_file) {
59 _gc = NULL;
60 _image = NULL;
61 _colormap = NULL;
62 _dirty_old = NULL;
63 _visual = gdk_visual_get_system();
64
65 _width = 0;
66 _height = 0;
67
68 _expose = false;
69 _w.status_sec = 0;
70
71 _cmd_ui_toggle = new CMD_toggle_main(this);
72
73 _accel_map_path = string(kcemu_homedir) + "/keys.map";
74
75 #if 0
76 switch (_visual->type) {
77 case GDK_VISUAL_STATIC_GRAY:
78 cout << "GDK_VISUAL_STATIC_GRAY" << endl;
79 break;
80 case GDK_VISUAL_GRAYSCALE:
81 cout << "GDK_VISUAL_GRAYSCALE" << endl;
82 break;
83 case GDK_VISUAL_STATIC_COLOR:
84 cout << "GDK_VISUAL_STATIC_COLOR" << endl;
85 break;
86 case GDK_VISUAL_PSEUDO_COLOR:
87 cout << "GDK_VISUAL_PSEUDO_COLOR" << endl;
88 break;
89 case GDK_VISUAL_TRUE_COLOR:
90 cout << "GDK_VISUAL_TRUE_COLOR" << endl;
91 break;
92 case GDK_VISUAL_DIRECT_COLOR:
93 cout << "GDK_VISUAL_DIRECT_COLOR" << endl;
94 break;
95 default:
96 cout << "unknown visual type" << endl;
97 break;
98 }
99 #endif
100 }
101
~MainWindow(void)102 MainWindow::~MainWindow(void) {
103 delete _cmd_ui_toggle;
104 }
105
106 void
attach_remote_listener(void)107 MainWindow::attach_remote_listener(void)
108 {
109 GdkAtom atom;
110
111 atom = gdk_atom_intern("_KCEMU_REMOTE_COMMAND", FALSE);
112 gdk_property_change(_window->window,
113 atom, GDK_TARGET_STRING, 8, GDK_PROP_MODE_REPLACE,
114 (unsigned char *) "", 1);
115 gdk_flush();
116 }
117
118 gboolean
on_property_change(GtkWidget * widget,GdkEventProperty * event,gpointer data)119 MainWindow::on_property_change(GtkWidget *widget, GdkEventProperty *event, gpointer data)
120 {
121 gboolean ret;
122 guchar *prop_data;
123 char *ptr, *val, *atom;
124 GdkAtom actual_property_type;
125 gint actual_format, actual_length;
126 CMD_Args *args;
127
128 MainWindow *self = (MainWindow *) data;
129
130 if (event == NULL)
131 return TRUE;
132
133 atom = gdk_atom_name(event->atom);
134 if (atom == NULL)
135 return TRUE;
136
137 if (strcmp(atom, "_KCEMU_REMOTE_COMMAND") == 0)
138 {
139 DBG(1, form("KCemu/UI/remote",
140 "property_change: %s\n",
141 atom));
142
143 prop_data = NULL;
144 ret = gdk_property_get(self->_window->window,
145 event->atom, GDK_TARGET_STRING,
146 0, (65536 / sizeof (long)), FALSE,
147 &actual_property_type,
148 &actual_format, &actual_length,
149 &prop_data);
150
151 if (!ret || (*prop_data == '\0'))
152 {
153 DBG(1, form("KCemu/UI/remote",
154 "empty or invalid property!\n"));
155 }
156 else
157 {
158 ptr = (char *) prop_data;
159 DBG(1, form("KCemu/UI/remote",
160 "command: %s'\n",
161 ptr));
162 args = new CMD_Args();
163 while (242)
164 {
165 ptr += strlen(ptr) + 1;
166 if ((ptr - (char *) prop_data) >= actual_length)
167 break;
168 val = strchr(ptr, '=');
169 if (!val)
170 continue;
171 *val++ = '\0';
172 DBG(1, form("KCemu/UI/remote",
173 " arg: %s -> '%s'\n",
174 ptr, val));
175 args->set_string_arg(ptr, val);
176 }
177
178 CMD_EXEC_ARGS((const char *) prop_data, args);
179 }
180
181 if (prop_data != NULL)
182 g_free(prop_data);
183
184 }
185
186 g_free(atom);
187
188 return TRUE;
189 }
190
191 gboolean
on_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)192 MainWindow::on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) {
193 static int x = 1;
194 MainWindow *self = (MainWindow *)user_data;
195
196 self->_expose = true;
197
198 if (x) {
199 x = 0;
200
201 self->attach_remote_listener();
202 Status::instance()->addStatusListener(self);
203
204 string greeting("KCemu v" KCEMU_VERSION " (");
205 greeting += Preferences::instance()->get_kc_variant_name();
206 greeting += ")";
207 Status::instance()->setMessage(greeting.c_str());
208 }
209
210 return FALSE; /* propagate event */
211 }
212
213 gboolean
on_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)214 MainWindow::on_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
215 MainWindow *self = (MainWindow *)user_data;
216
217 switch (event->button) {
218 case 2: {
219 /* hmm, is TEXT the correct selection type ??? */
220 GdkAtom atom = gdk_atom_intern("TEXT", FALSE);
221 if (atom == GDK_NONE)
222 break;
223 gtk_selection_convert(self->_window, GDK_SELECTION_PRIMARY, atom, GDK_CURRENT_TIME);
224 break;
225 }
226 case 3: {
227 gtk_menu_popup(GTK_MENU(self->_w.popup_menu), NULL, NULL, NULL, NULL, 3, event->time);
228 break;
229 }
230 }
231
232 return FALSE; /* propagate event */
233 }
234
235 void
on_accel_group_changed(GtkAccelGroup * accel_group,guint keyval,GdkModifierType modifier,GClosure * accel_closure,gpointer user_data)236 MainWindow::on_accel_group_changed(GtkAccelGroup *accel_group, guint keyval, GdkModifierType modifier, GClosure *accel_closure, gpointer user_data) {
237 MainWindow *self = (MainWindow *)user_data;
238 if (self->_w.idle_id == 0) {
239 // prevent multiple saves as a single user change might cause many signals
240 self->_w.idle_id = g_idle_add(on_accel_group_changed_idle_func, self);
241 }
242 }
243
244 gboolean
on_accel_group_changed_idle_func(gpointer data)245 MainWindow::on_accel_group_changed_idle_func(gpointer data) {
246 MainWindow *self = (MainWindow *)data;
247 gtk_accel_map_save(self->_accel_map_path.c_str());
248 self->_w.idle_id = 0;
249 return FALSE; /* remove from idle list */
250 }
251
252 GtkWidget *
get_main_window(void)253 MainWindow::get_main_window(void) {
254 return _window;
255 }
256
257 void
wire_menu_item(const char * name,const char * shortcut,const char * command)258 MainWindow::wire_menu_item(const char *name, const char *shortcut, const char *command) {
259 GtkMenuItem *item = GTK_MENU_ITEM(get_widget(name));
260
261 GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(item));
262 const char *parent_name = gtk_widget_get_name(parent);
263 string path = string("<KCemu>/") + parent_name + "/" + name;
264
265 gtk_menu_item_set_accel_path(item, path.c_str());
266 g_signal_connect(item, "activate", G_CALLBACK(cmd_exec_mc), (gpointer)(command));
267
268 string popup_name = string(name) + "_p";
269 GtkMenuItem *popup_item = GTK_MENU_ITEM(get_widget_or_null(popup_name.c_str()));
270 if (popup_item != NULL) {
271 gtk_menu_item_set_accel_path(popup_item, path.c_str());
272 g_signal_connect(popup_item, "activate", G_CALLBACK(cmd_exec_mc), (gpointer)(command));
273 }
274
275 if (shortcut != NULL) {
276 guint accel_key;
277 GdkModifierType accel_mods;
278 gtk_accelerator_parse(shortcut, &accel_key, &accel_mods);
279
280 gtk_accel_map_add_entry(path.c_str(), accel_key, accel_mods);
281 }
282 }
283
284 void
add_menu_accel_group(const char * base,const char * name)285 MainWindow::add_menu_accel_group(const char *base, const char *name) {
286 string path = string(base) + "/" + name;
287 GtkMenu *menu = GTK_MENU(get_widget(name));
288 gtk_menu_set_accel_group(menu, _w.accel_group);
289 //gtk_menu_set_accel_path(menu, path.c_str());
290 }
291
292 gboolean
get_display_effect(void)293 MainWindow::get_display_effect(void) {
294 return kcemu_ui_display_effect;
295 }
296
297 void
set_display_effect(gboolean effect)298 MainWindow::set_display_effect(gboolean effect) {
299 GtkWidget *widget = get_widget("menuitem_display_effects");
300 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), effect);
301 kcemu_ui_display_effect = effect;
302 }
303
304 void
init(void)305 MainWindow::init(void) {
306 _window = get_widget("main_window");
307 g_signal_connect(_window, "delete-event", G_CALLBACK(cmd_exec_sft), (gpointer)"emu-quit");
308 g_signal_connect(_window, "property-notify-event", G_CALLBACK(on_property_change), this);
309
310 _w.accel_group = gtk_accel_group_new();
311 gtk_window_add_accel_group(GTK_WINDOW(_window), _w.accel_group);
312
313 init_icon();
314
315 string title(Preferences::instance()->get_kc_type_name());
316 title += " Emulator";
317 gtk_window_set_title(GTK_WINDOW(_window), title.c_str());
318
319 _w.menubar = GTK_MENU_BAR(get_widget("menubar"));
320 _w.status_hbox = GTK_HBOX(get_widget("status_hbox"));
321
322 _w.drawingarea = GTK_DRAWING_AREA(get_widget("drawingarea"));
323 _w.popup_menu = GTK_MENU(get_widget("menu_popup"));
324
325 _w.status_label = GTK_LABEL(get_widget("status_label"));
326 _w.status_statusbar = GTK_STATUSBAR(get_widget("status_statusbar"));
327
328 g_signal_connect(_w.drawingarea, "expose_event", G_CALLBACK(on_expose_event), this);
329 g_signal_connect(_window, "button-press-event", G_CALLBACK(on_button_press_event), this);
330
331 add_menu_accel_group("<KCemu>", "menu_emulator");
332 add_menu_accel_group("<KCemu>", "menu_view");
333 add_menu_accel_group("<KCemu>", "menu_options");
334 add_menu_accel_group("<KCemu>", "menu_popup");
335 add_menu_accel_group("<KCemu>", "menu_view_p");
336
337 GtkMenuItem *helpitem = GTK_MENU_ITEM(get_widget("menubaritem_help"));
338 gtk_menu_item_set_right_justified(helpitem, TRUE);
339
340 // initialize before attaching signal handlers
341 set_display_effect(get_display_effect());
342
343 wire_menu_item("menuitem_run", NULL, "kc-image-run");
344 wire_menu_item("menuitem_load", "<alt>L", "kc-image-load");
345 wire_menu_item("menuitem_save", "<alt>S", "ui-save-memory-window-toggle");
346 wire_menu_item("menuitem_tape", "<alt>T", "ui-tape-window-toggle");
347 wire_menu_item("menuitem_disk", "<alt>D", "ui-disk-window-toggle");
348 wire_menu_item("menuitem_vdip", "<alt>V", "ui-vdip-window-toggle");
349 wire_menu_item("menuitem_module", "<alt>M", "ui-module-window-toggle");
350 wire_menu_item("menuitem_audio", "<alt>U", "ui-wav-window-toggle");
351 wire_menu_item("menuitem_screenshot", "<alt>H", "ui-screenshot-window-toggle");
352 wire_menu_item("menuitem_plotter", "<alt>O", "ui-plotter-window-toggle");
353 wire_menu_item("menuitem_video", NULL, "ui-video-window-toggle");
354 wire_menu_item("menuitem_reset", "<alt>R", "emu-reset");
355 wire_menu_item("menuitem_power", "<alt>P", "emu-power-on");
356 wire_menu_item("menuitem_quit", "<alt>Q", "emu-quit");
357 wire_menu_item("menuitem_zoom1", "<alt>1", "ui-zoom-1");
358 wire_menu_item("menuitem_zoom2", "<alt>2", "ui-zoom-2");
359 wire_menu_item("menuitem_zoom3", "<alt>3", "ui-zoom-3");
360 wire_menu_item("menuitem_keyboard", "<alt>K", "ui-keyboard-window-toggle");
361 wire_menu_item("menuitem_menubar", NULL, "ui-menu-bar-toggle");
362 wire_menu_item("menuitem_statusbar", NULL, "ui-status-bar-toggle");
363 wire_menu_item("menuitem_colors", "<alt>C", "ui-color-window-toggle");
364 wire_menu_item("menuitem_display_effects", NULL, "ui-display-effects-toggle");
365 wire_menu_item("menuitem_no_speed_limit", "<alt>G", "ui-speed-limit-toggle");
366 wire_menu_item("menuitem_profiles", NULL, "ui-options-window-toggle");
367 wire_menu_item("menuitem_help", NULL, "ui-help-window-toggle-home");
368 wire_menu_item("menuitem_help_index", NULL, "ui-help-window-toggle-index");
369 wire_menu_item("menuitem_context_help", NULL, "ui-help-window-context-help");
370 wire_menu_item("menuitem_about", NULL, "ui-about-window-toggle");
371 wire_menu_item("menuitem_thanks", NULL, "ui-thanks-window-toggle");
372 wire_menu_item("menuitem_licence", NULL, "ui-copying-window-toggle");
373 wire_menu_item("menuitem_no_warranty", NULL, "ui-warranty-window-toggle");
374
375 gtk_accel_map_load(_accel_map_path.c_str());
376 g_signal_connect(_w.accel_group, "accel-changed", G_CALLBACK(on_accel_group_changed), this);
377 }
378
379 void
init_icon(void)380 MainWindow::init_icon(void) {
381 const EmulationType &emulation_type = Preferences::instance()->get_system_type()->get_emulation_type();
382 GdkPixbuf *icon = get_icon(emulation_type.get_icon_name());
383 if (icon != NULL)
384 gtk_window_set_icon(GTK_WINDOW(_window), icon);
385
386 g_object_set_data(G_OBJECT(_window), "help-topic", (gpointer)emulation_type.get_help_topic());
387 }
388
389 void
process_events(void)390 MainWindow::process_events(void) {
391 if (_w.status_sec == 0)
392 return;
393
394 long tv_sec, tv_usec;
395
396 sys_gettimeofday(&tv_sec, &tv_usec);
397 if (tv_sec - _w.status_sec > 10) {
398 gtk_statusbar_pop(_w.status_statusbar, 1);
399 }
400 }
401
402 void
show(int width,int height)403 MainWindow::show(int width, int height) {
404 init();
405 gtk_drawing_area_size(_w.drawingarea, width, height);
406 UI_Gtk_Window::show();
407 }
408
409 bool
resize(int width,int height)410 MainWindow::resize(int width, int height) {
411 if ((_width == width) && (_height == height))
412 return false;
413
414 if (_image)
415 gdk_image_destroy(_image);
416 _image = gdk_image_new(GDK_IMAGE_FASTEST, _visual, width, height);
417
418 gtk_drawing_area_size(_w.drawingarea, width, height);
419
420 if (!GTK_WIDGET_VISIBLE(_window))
421 gtk_widget_show(_window);
422
423 if (_gc)
424 gdk_gc_destroy(_gc);
425
426 _gc = gdk_gc_new(GTK_WIDGET(_w.drawingarea)->window);
427
428 // force reallocation of dirty buffer
429 if (_dirty_old != NULL) {
430 delete _dirty_old;
431 _dirty_old = NULL;
432 }
433
434 return true;
435 }
436
437 void
allocate_color_rgb(int idx,int r,int g,int b)438 MainWindow::allocate_color_rgb(int idx, int r, int g, int b) {
439 _col[idx].red = r << 8;
440 _col[idx].green = g << 8;
441 _col[idx].blue = b << 8;
442 _colormap = gdk_colormap_get_system();
443 gdk_color_alloc(_colormap, &_col[idx]);
444 }
445
446 static gulong
lighter_color(gulong col)447 lighter_color(gulong col) {
448 static int color_add = 50; // RC::instance()->get_int("DEBUG UI_Gtk Color Add", 50);
449
450 int r = (col >> 16) & 0xff;
451 int g = (col >> 8) & 0xff;
452 int b = (col ) & 0xff;
453
454 r += color_add;
455 g += color_add;
456 b += color_add;
457
458 if (r > 255)
459 r = 255;
460 if (g > 255)
461 g = 255;
462 if (b > 255)
463 b = 255;
464
465 return (r << 16) | (g << 8) | b;
466 }
467
468 static gulong
darker_color(gulong col)469 darker_color(gulong col) {
470 int r = (col >> 16) & 0xff;
471 int g = (col >> 8) & 0xff;
472 int b = (col ) & 0xff;
473
474 r = (2 * r) / 3;
475 g = (2 * g) / 3;
476 b = (2 * b) / 3;
477
478 return (r << 16) | (g << 8) | b;
479 }
480
481 #define ADD_COL(weight) \
482 r += weight * ((p >> 16) & 0xff); \
483 g += weight * ((p >> 8) & 0xff); \
484 b += weight * ((p ) & 0xff); \
485 w += weight
486
487 /*
488 * +---+---+---+
489 * | 0 | 1 | 2 |
490 * +---+---+---+
491 * | 3 | 4 | 5 |
492 * +---+---+---+
493 * | 6 | 7 | 8 |
494 * +---+---+---+
495 */
496 gulong
get_col(byte_t * bitmap,int which,int idx,int width)497 MainWindow::get_col(byte_t *bitmap, int which, int idx, int width) {
498 gulong p;
499 long r, g, b, w;
500
501 w = 0; r = 0; g = 0; b = 0;
502
503 switch (which) {
504 default:
505 return _col[bitmap[idx]].pixel;
506
507 case 1:
508 p = _col[bitmap[idx]].pixel; ADD_COL(9);
509 p = _col[bitmap[idx - width]].pixel; ADD_COL(3);
510 break;
511 case 3:
512 p = _col[bitmap[idx]].pixel; ADD_COL(9);
513 p = _col[bitmap[idx - 1]].pixel; ADD_COL(3);
514 break;
515 case 5:
516 p = _col[bitmap[idx]].pixel; ADD_COL(9);
517 p = _col[bitmap[idx + 1]].pixel; ADD_COL(3);
518 break;
519 case 7:
520 p = _col[bitmap[idx]].pixel; ADD_COL(9);
521 p = _col[bitmap[idx + width]].pixel; ADD_COL(3);
522 break;
523
524 case 0:
525 p = _col[bitmap[idx]].pixel; ADD_COL(12);
526 p = _col[bitmap[idx - 1]].pixel; ADD_COL(5);
527 p = _col[bitmap[idx - width]].pixel; ADD_COL(5);
528 p = _col[bitmap[idx - width - 1]].pixel; ADD_COL(1);
529 break;
530 case 2:
531 p = _col[bitmap[idx]].pixel; ADD_COL(12);
532 p = _col[bitmap[idx + 1]].pixel; ADD_COL(5);
533 p = _col[bitmap[idx - width]].pixel; ADD_COL(5);
534 p = _col[bitmap[idx - width + 1]].pixel; ADD_COL(1);
535 break;
536 case 6:
537 p = _col[bitmap[idx]].pixel; ADD_COL(12);
538 p = _col[bitmap[idx - 1]].pixel; ADD_COL(5);
539 p = _col[bitmap[idx + width]].pixel; ADD_COL(5);
540 p = _col[bitmap[idx + width - 1]].pixel; ADD_COL(1);
541 break;
542 case 8:
543 p = _col[bitmap[idx]].pixel; ADD_COL(12);
544 p = _col[bitmap[idx + 1]].pixel; ADD_COL(5);
545 p = _col[bitmap[idx + width]].pixel; ADD_COL(5);
546 p = _col[bitmap[idx + width + 1]].pixel; ADD_COL(1);
547 break;
548 }
549
550 r = r / w;
551 g = g / w;
552 b = b / w;
553
554 return (r << 16) | (g << 8) | b;
555 }
556
557 void
update_1(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)558 MainWindow::update_1(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
559 int d = -1;
560 for (int y = 0;y < height;y += 8) {
561 for (int x = 0;x < width;x += 8) {
562 d++;
563 if (!dirty[d])
564 continue;
565
566 int z = y * width + x;
567
568 for (int yy = 0;yy < 8;yy++) {
569 for (int xx = 0;xx < 8;xx++) {
570 gdk_image_put_pixel(_image, x + xx, y + yy, _col[bitmap[z + xx]].pixel);
571 }
572 z += width;
573 }
574 }
575 }
576 }
577
578 void
update_1_debug(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)579 MainWindow::update_1_debug(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
580 static int frame_delay = 50;
581
582 if (_dirty_old == NULL) {
583 _dirty_old = new byte_t[dirty_size];
584 memset(_dirty_old, 0, dirty_size);
585 //frame_delay = 50; // RC::instance()->get_int("DEBUG UI_Gtk Frame Delay", 50);
586 }
587
588 int d = -1;
589 for (int y = 0;y < height;y += 8) {
590 for (int x = 0;x < width;x += 8) {
591 d++;
592 if (dirty[d])
593 _dirty_old[d] = frame_delay;
594
595 if (_dirty_old[d] == 0)
596 continue;
597
598 if (_dirty_old[d] > 0)
599 _dirty_old[d]--;
600
601 dirty[d] = 1;
602
603 int z = y * width + x;
604
605 if (_dirty_old[d]) {
606 for (int yy = 0;yy < 8;yy++) {
607 for (int xx = 0;xx < 8;xx++) {
608 gdk_image_put_pixel(_image, x + xx, y + yy, lighter_color(_col[bitmap[z + xx]].pixel));
609 }
610 z += width;
611 }
612 }
613 else {
614 for (int yy = 0;yy < 8;yy++) {
615 for (int xx = 0;xx < 8;xx++) {
616 gdk_image_put_pixel(_image, x + xx, y + yy, _col[bitmap[z + xx]].pixel);
617 }
618 z += width;
619 }
620 }
621 }
622 }
623 }
624
625 void
update_2(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)626 MainWindow::update_2(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
627 int d = -1;
628 for (int y = 0;y < height;y += 8) {
629 for (int x = 0;x < width;x += 8) {
630 d++;
631 if (!dirty[d])
632 continue;
633
634 int z = y * width + x;
635
636 for (int yy = 0;yy < 16;yy += 2) {
637 for (int xx = 0;xx < 16;xx += 2) {
638 gulong pix = _col[bitmap[z++]].pixel;
639 gdk_image_put_pixel(_image, 2 * x + xx, 2 * y + yy , pix);
640 gdk_image_put_pixel(_image, 2 * x + xx + 1, 2 * y + yy , pix);
641 gdk_image_put_pixel(_image, 2 * x + xx , 2 * y + yy + 1, pix);
642 gdk_image_put_pixel(_image, 2 * x + xx + 1, 2 * y + yy + 1, pix);
643 }
644 z += width - 8;
645 }
646 }
647 }
648 }
649
650 void
update_2_scanline(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)651 MainWindow::update_2_scanline(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
652 int d = -1;
653 for (int y = 0;y < height;y += 8) {
654 for (int x = 0;x < width;x += 8) {
655 d++;
656 if (!dirty[d])
657 continue;
658
659 int z = y * width + x;
660
661 for (int yy = 0;yy < 16;yy += 2) {
662 for (int xx = 0;xx < 16;xx += 2) {
663 gulong pix = _col[bitmap[z++]].pixel;
664 gdk_image_put_pixel(_image, 2 * x + xx, 2 * y + yy , pix);
665 gdk_image_put_pixel(_image, 2 * x + xx + 1, 2 * y + yy , pix);
666 gdk_image_put_pixel(_image, 2 * x + xx , 2 * y + yy + 1, darker_color(pix));
667 gdk_image_put_pixel(_image, 2 * x + xx + 1, 2 * y + yy + 1, darker_color(pix));
668 }
669 z += width - 8;
670 }
671 }
672 }
673 }
674
675 void
update_3(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)676 MainWindow::update_3(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
677 int d = -1;
678 for (int y = 0;y < height;y += 8) {
679 for (int x = 0;x < width;x += 8) {
680 d++;
681 if (!dirty[d])
682 continue;
683
684 int z = y * width + x;
685
686 for (int yy = 0;yy < 24;yy += 3) {
687 for (int xx = 0;xx < 24;xx += 3) {
688 gulong pix = _col[bitmap[z++]].pixel;
689 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy , pix);
690 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy , pix);
691 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy , pix);
692 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy + 1, pix);
693 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy + 1, pix);
694 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy + 1, pix);
695 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy + 2, pix);
696 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy + 2, pix);
697 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy + 2, pix);
698 }
699 z += width - 8;
700 }
701 }
702 }
703 }
704
705 void
update_3_smooth(byte_t * bitmap,byte_t * dirty,int dirty_size,int width,int height)706 MainWindow::update_3_smooth(byte_t *bitmap, byte_t *dirty, int dirty_size, int width, int height) {
707 int d = -1;
708 byte_t dirty_buf[dirty_size];
709
710 memcpy(dirty_buf, dirty, dirty_size);
711
712 for (int y = 0;y < height;y += 8) {
713 for (int x = 0;x < width;x += 8) {
714 d++;
715
716 if (dirty[d]) {
717 int z = y * width + x;
718
719 for (int yy = 0;yy < 24;yy += 3) {
720 for (int xx = 0;xx < 24;xx += 3) {
721 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy , get_col(bitmap, 0, z, width));
722 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy , get_col(bitmap, 1, z, width));
723 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy , get_col(bitmap, 2, z, width));
724 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy + 1, get_col(bitmap, 3, z, width));
725 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy + 1, get_col(bitmap, 4, z, width));
726 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy + 1, get_col(bitmap, 5, z, width));
727 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + yy + 2, get_col(bitmap, 6, z, width));
728 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + yy + 2, get_col(bitmap, 7, z, width));
729 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + yy + 2, get_col(bitmap, 8, z, width)); z++;
730 }
731 z += width - 8;
732 }
733 }
734 else {
735 /*
736 * not dirty but we need to check the neighbour pixels due to
737 * the antialiasing
738 */
739 if ((d > 0) && (dirty[d - 1])) {
740 int z = y * width + x;
741 for (int yy = 0;yy < 24;yy += 3) {
742 gdk_image_put_pixel(_image, 3 * x, 3 * y + yy , get_col(bitmap, 0, z, width));
743 gdk_image_put_pixel(_image, 3 * x, 3 * y + yy + 1, get_col(bitmap, 3, z, width));
744 gdk_image_put_pixel(_image, 3 * x, 3 * y + yy + 2, get_col(bitmap, 6, z, width));
745 z += width;
746 }
747 dirty_buf[d] = 1;
748 }
749 if (dirty[d + 1]) {
750 int z = y * width + x + 7;
751 for (int yy = 0;yy < 24;yy += 3) {
752 gdk_image_put_pixel(_image, 3 * x + 23, 3 * y + yy , get_col(bitmap, 2, z, width));
753 gdk_image_put_pixel(_image, 3 * x + 23, 3 * y + yy + 1, get_col(bitmap, 5, z, width));
754 gdk_image_put_pixel(_image, 3 * x + 23, 3 * y + yy + 2, get_col(bitmap, 8, z, width));
755 z += width;
756 }
757 dirty_buf[d] = 1;
758 }
759 if (dirty[d + width / 8]) {
760 int z = (y + 7) * width + x;
761 for (int xx = 0;xx < 24;xx += 3) {
762 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y + 23, get_col(bitmap, 6, z, width));
763 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y + 23, get_col(bitmap, 7, z, width));
764 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y + 23, get_col(bitmap, 8, z, width));
765 z++;
766 }
767 dirty_buf[d] = 1;
768 }
769 if ((d > width / 8) && (dirty[d - width / 8])) {
770 int z = y * width + x;
771 for (int xx = 0;xx < 24;xx += 3) {
772 gdk_image_put_pixel(_image, 3 * x + xx , 3 * y, get_col(bitmap, 0, z, width));
773 gdk_image_put_pixel(_image, 3 * x + xx + 1, 3 * y, get_col(bitmap, 1, z, width));
774 gdk_image_put_pixel(_image, 3 * x + xx + 2, 3 * y, get_col(bitmap, 2, z, width));
775 z++;
776 }
777 dirty_buf[d] = 1;
778 }
779 }
780 }
781 }
782
783 memcpy(dirty, dirty_buf, dirty_size);
784 }
785
786 void
update(UI_Base * ui,int image_width,int image_height,bool full_update)787 MainWindow::update(UI_Base *ui, int image_width, int image_height, bool full_update) {
788
789 byte_t *bitmap = ui->get_buffer();
790 byte_t *dirty = ui->get_dirty_buffer();
791 int dirty_size = ui->get_dirty_buffer_size();
792 int width = ui->get_real_width();
793 int height = ui->get_real_height();
794
795 switch (kcemu_ui_scale) {
796 case 1:
797 if (kcemu_ui_debug)
798 update_1_debug(bitmap, dirty, dirty_size, width, height);
799 else
800 update_1(bitmap, dirty, dirty_size, width, height);
801 break;
802 case 2:
803 if (kcemu_ui_display_effect)
804 update_2_scanline(bitmap, dirty, dirty_size, width, height);
805 else
806 update_2(bitmap, dirty, dirty_size, width, height);
807 break;
808 case 3:
809 if (kcemu_ui_display_effect)
810 update_3_smooth(bitmap, dirty, dirty_size, width, height);
811 else
812 update_3(bitmap, dirty, dirty_size, width, height);
813 break;
814 }
815
816 if (full_update || _expose) {
817 _expose = false;
818 gdk_draw_image(GTK_WIDGET(_w.drawingarea)->window, _gc, _image,
819 0, 0, 0, 0, image_width, image_height);
820 return;
821 }
822
823 int d = -1;
824 int s = 8 * kcemu_ui_scale;
825 for (int y = 0;y < image_height;y += s) {
826 for (int x = 0;x < image_width;x += s) {
827 d++;
828 if (!dirty[d])
829 continue;
830
831 gdk_draw_image(GTK_WIDGET(_w.drawingarea)->window, _gc, _image, x, y, x, y, s, s);
832 }
833 }
834 }
835
836 void
set_fps(unsigned long fps)837 MainWindow::set_fps(unsigned long fps) {
838 char buf[20];
839 snprintf(buf, sizeof(buf), " %ld fps ", fps);
840 gtk_label_set(_w.status_label, buf);
841 }
842
843 void
status_bar_toggle(void)844 MainWindow::status_bar_toggle(void) {
845 if (GTK_WIDGET_VISIBLE(_w.status_hbox))
846 gtk_widget_hide(GTK_WIDGET(_w.status_hbox));
847 else
848 gtk_widget_show(GTK_WIDGET(_w.status_hbox));
849 }
850
851 void
menu_bar_toggle(void)852 MainWindow::menu_bar_toggle(void) {
853 if (GTK_WIDGET_VISIBLE(_w.menubar))
854 gtk_widget_hide(GTK_WIDGET(_w.menubar));
855 else
856 gtk_widget_show(GTK_WIDGET(_w.menubar));
857 }
858
859 void
setStatus(const char * msg)860 MainWindow::setStatus(const char *msg) {
861 long tv_sec, tv_usec;
862
863 sys_gettimeofday(&tv_sec, &tv_usec);
864 _w.status_sec = tv_sec;
865 gtk_statusbar_pop(GTK_STATUSBAR(_w.status_statusbar), 1);
866 gtk_statusbar_push(GTK_STATUSBAR(_w.status_statusbar), 1, msg);
867 }
868
869 GdkColor *
get_colormap()870 MainWindow::get_colormap()
871 {
872 return _col;
873 }