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 }