1 /////////////////////////////////////////////////////////////////////////
2 // $Id: gui.cc 14274 2021-06-07 11:30:08Z vruppert $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2002-2021  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 
21 
22 #include <signal.h>
23 #include "iodev.h"
24 #include "virt_timer.h"
25 #include "keymap.h"
26 #include "gui/bitmaps/floppya.h"
27 #include "gui/bitmaps/floppyb.h"
28 #include "gui/bitmaps/mouse.h"
29 #include "gui/bitmaps/reset.h"
30 #include "gui/bitmaps/power.h"
31 #include "gui/bitmaps/snapshot.h"
32 #include "gui/bitmaps/copy.h"
33 #include "gui/bitmaps/paste.h"
34 #include "gui/bitmaps/configbutton.h"
35 #include "gui/bitmaps/cdromd.h"
36 #include "gui/bitmaps/userbutton.h"
37 #include "gui/bitmaps/saverestore.h"
38 
39 #if BX_USE_GUI_CONSOLE
40 #include "sdl.h"
41 #endif
42 
43 #if BX_WITH_MACOS
44 #  include <Disks.h>
45 #endif
46 
47 bx_gui_c *bx_gui = NULL;
48 
49 #define BX_GUI_THIS bx_gui->
50 #define LOG_THIS BX_GUI_THIS
51 
52 // Enable this if you want confirm quit after pressing power button.
53 //#define BX_GUI_CONFIRM_QUIT
54 
55 // user button shortcut stuff
56 
57 #define BX_KEY_UNKNOWN 0x7fffffff
58 #define N_USER_KEYS 38
59 
60 typedef struct {
61   const char *key;
62   Bit32u symbol;
63 } user_key_t;
64 
65 static user_key_t user_keys[N_USER_KEYS] =
66 {
67   { "f1",    BX_KEY_F1 },
68   { "f2",    BX_KEY_F2 },
69   { "f3",    BX_KEY_F3 },
70   { "f4",    BX_KEY_F4 },
71   { "f5",    BX_KEY_F5 },
72   { "f6",    BX_KEY_F6 },
73   { "f7",    BX_KEY_F7 },
74   { "f8",    BX_KEY_F8 },
75   { "f9",    BX_KEY_F9 },
76   { "f10",   BX_KEY_F10 },
77   { "f11",   BX_KEY_F11 },
78   { "f12",   BX_KEY_F12 },
79   { "alt",   BX_KEY_ALT_L },
80   { "bksl",  BX_KEY_BACKSLASH },
81   { "bksp",  BX_KEY_BACKSPACE },
82   { "ctrl",  BX_KEY_CTRL_L },
83   { "del",   BX_KEY_DELETE },
84   { "down",  BX_KEY_DOWN },
85   { "end",   BX_KEY_END },
86   { "enter", BX_KEY_ENTER },
87   { "esc",   BX_KEY_ESC },
88   { "home",  BX_KEY_HOME },
89   { "ins",   BX_KEY_INSERT },
90   { "left",  BX_KEY_LEFT },
91   { "menu",  BX_KEY_MENU },
92   { "minus", BX_KEY_MINUS },
93   { "pgdwn", BX_KEY_PAGE_DOWN },
94   { "pgup",  BX_KEY_PAGE_UP },
95   { "plus",  BX_KEY_KP_ADD },
96   { "right", BX_KEY_RIGHT },
97   { "shift", BX_KEY_SHIFT_L },
98   { "space", BX_KEY_SPACE },
99   { "tab",   BX_KEY_TAB },
100   { "up",    BX_KEY_UP },
101   { "win",   BX_KEY_WIN_L },
102   { "print", BX_KEY_PRINT },
103   { "power", BX_KEY_POWER_POWER },
104   { "scrlck", BX_KEY_SCRL_LOCK }
105 };
106 
get_user_key(char * key)107 Bit32u get_user_key(char *key)
108 {
109   int i = 0;
110 
111   while (i < N_USER_KEYS) {
112     if (!strcmp(key, user_keys[i].key))
113       return user_keys[i].symbol;
114     i++;
115   }
116   return BX_KEY_UNKNOWN;
117 }
118 
119 // font bitmap helper function
120 
reverse_bitorder(Bit8u b)121 Bit8u reverse_bitorder(Bit8u b)
122 {
123   Bit8u ret = 0;
124 
125   for (unsigned i = 0; i < 8; i++) {
126     ret |= (b & 0x01) << (7 - i);
127     b >>= 1;
128   }
129   return ret;
130 }
131 
132 // common gui implementation
133 
bx_gui_c(void)134 bx_gui_c::bx_gui_c(void): disp_mode(DISP_MODE_SIM)
135 {
136   put("GUI"); // Init in specific_init
137   bx_headerbar_entries = 0;
138   statusitem_count = 0;
139   led_timer_index = BX_NULL_TIMER_HANDLE;
140   framebuffer = NULL;
141   guest_textmode = 1;
142   guest_fwidth = 8;
143   guest_fheight = 16;
144   guest_xres = 640;
145   guest_yres = 480;
146   guest_bpp = 8;
147   snapshot_mode = 0;
148   snapshot_buffer = NULL;
149   command_mode.present = 0;
150   command_mode.active = 0;
151   marker_count = 0;
152   memset(palette, 0, sizeof(palette));
153   memset(vga_charmap, 0, 0x2000);
154 }
155 
~bx_gui_c()156 bx_gui_c::~bx_gui_c()
157 {
158   if (framebuffer != NULL) {
159     delete [] framebuffer;
160   }
161 #if BX_USE_GUI_CONSOLE
162   if (console.running) {
163     console_cleanup();
164   }
165 #endif
166 }
167 
init(int argc,char ** argv,unsigned max_xres,unsigned max_yres,unsigned tilewidth,unsigned tileheight)168 void bx_gui_c::init(int argc, char **argv, unsigned max_xres, unsigned max_yres,
169                     unsigned tilewidth, unsigned tileheight)
170 {
171   BX_GUI_THIS new_gfx_api = 0;
172   BX_GUI_THIS host_xres = 640;
173   BX_GUI_THIS host_yres = 480;
174   BX_GUI_THIS host_bpp = 8;
175   BX_GUI_THIS new_text_api = 0;
176   memset(&BX_GUI_THIS tm_info, 0, sizeof(bx_vga_tminfo_t));
177   BX_GUI_THIS cursor_address = 0;
178   BX_GUI_THIS max_xres = max_xres;
179   BX_GUI_THIS max_yres = max_yres;
180   BX_GUI_THIS x_tilesize = tilewidth;
181   BX_GUI_THIS y_tilesize = tileheight;
182   BX_GUI_THIS dialog_caps = BX_GUI_DLG_RUNTIME | BX_GUI_DLG_SAVE_RESTORE;
183 #if BX_USE_GUI_CONSOLE
184   BX_GUI_THIS console.present = 0;
185   BX_GUI_THIS console.running = 0;
186 #endif
187   BX_GUI_THIS toggle_method = SIM->get_param_enum(BXPN_MOUSE_TOGGLE)->get();
188   BX_GUI_THIS toggle_keystate = 0;
189   switch (toggle_method) {
190     case BX_MOUSE_TOGGLE_CTRL_MB:
191       strcpy(mouse_toggle_text, "CTRL + 3rd button");
192       break;
193     case BX_MOUSE_TOGGLE_CTRL_F10:
194       strcpy(mouse_toggle_text, "CTRL + F10");
195       break;
196     case BX_MOUSE_TOGGLE_CTRL_ALT:
197       strcpy(mouse_toggle_text, "CTRL + ALT");
198       break;
199     case BX_MOUSE_TOGGLE_F12:
200       strcpy(mouse_toggle_text, "F12");
201       break;
202   }
203 
204   specific_init(argc, argv, BX_HEADER_BAR_Y);
205 
206   // Define some bitmaps to use in the headerbar
207   BX_GUI_THIS floppyA_bmap_id = create_bitmap(bx_floppya_bmap,
208                           BX_FLOPPYA_BMAP_X, BX_FLOPPYA_BMAP_Y);
209   BX_GUI_THIS floppyA_eject_bmap_id = create_bitmap(bx_floppya_eject_bmap,
210                           BX_FLOPPYA_BMAP_X, BX_FLOPPYA_BMAP_Y);
211   BX_GUI_THIS floppyB_bmap_id = create_bitmap(bx_floppyb_bmap,
212                           BX_FLOPPYB_BMAP_X, BX_FLOPPYB_BMAP_Y);
213   BX_GUI_THIS floppyB_eject_bmap_id = create_bitmap(bx_floppyb_eject_bmap,
214                           BX_FLOPPYB_BMAP_X, BX_FLOPPYB_BMAP_Y);
215   BX_GUI_THIS cdrom1_bmap_id = create_bitmap(bx_cdromd_bmap,
216                           BX_CDROMD_BMAP_X, BX_CDROMD_BMAP_Y);
217   BX_GUI_THIS cdrom1_eject_bmap_id = create_bitmap(bx_cdromd_eject_bmap,
218                           BX_CDROMD_BMAP_X, BX_CDROMD_BMAP_Y);
219   BX_GUI_THIS mouse_bmap_id = create_bitmap(bx_mouse_bmap,
220                           BX_MOUSE_BMAP_X, BX_MOUSE_BMAP_Y);
221   BX_GUI_THIS nomouse_bmap_id = create_bitmap(bx_nomouse_bmap,
222                           BX_MOUSE_BMAP_X, BX_MOUSE_BMAP_Y);
223 
224   BX_GUI_THIS power_bmap_id = create_bitmap(bx_power_bmap, BX_POWER_BMAP_X, BX_POWER_BMAP_Y);
225   BX_GUI_THIS reset_bmap_id = create_bitmap(bx_reset_bmap, BX_RESET_BMAP_X, BX_RESET_BMAP_Y);
226   BX_GUI_THIS snapshot_bmap_id = create_bitmap(bx_snapshot_bmap, BX_SNAPSHOT_BMAP_X, BX_SNAPSHOT_BMAP_Y);
227   BX_GUI_THIS copy_bmap_id = create_bitmap(bx_copy_bmap, BX_COPY_BMAP_X, BX_COPY_BMAP_Y);
228   BX_GUI_THIS paste_bmap_id = create_bitmap(bx_paste_bmap, BX_PASTE_BMAP_X, BX_PASTE_BMAP_Y);
229   BX_GUI_THIS config_bmap_id = create_bitmap(bx_config_bmap, BX_CONFIG_BMAP_X, BX_CONFIG_BMAP_Y);
230   BX_GUI_THIS user_bmap_id = create_bitmap(bx_user_bmap, BX_USER_BMAP_X, BX_USER_BMAP_Y);
231   BX_GUI_THIS save_restore_bmap_id = create_bitmap(bx_save_restore_bmap,
232                           BX_SAVE_RESTORE_BMAP_X, BX_SAVE_RESTORE_BMAP_Y);
233 
234   // Add the initial bitmaps to the headerbar, and enable callback routine, for use
235   // when that bitmap is clicked on. The floppy and cdrom devices are not
236   // initialized yet. so we just set the bitmaps to ejected for now.
237 
238   // Floppy A:
239   BX_GUI_THIS floppyA_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyA_eject_bmap_id,
240                           BX_GRAVITY_LEFT, floppyA_handler);
241   BX_GUI_THIS set_tooltip(BX_GUI_THIS floppyA_hbar_id, "Change floppy A: media");
242 
243   // Floppy B:
244   BX_GUI_THIS floppyB_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyB_eject_bmap_id,
245                           BX_GRAVITY_LEFT, floppyB_handler);
246   BX_GUI_THIS set_tooltip(BX_GUI_THIS floppyB_hbar_id, "Change floppy B: media");
247 
248   // First CD-ROM
249   BX_GUI_THIS cdrom1_hbar_id = headerbar_bitmap(BX_GUI_THIS cdrom1_eject_bmap_id,
250                           BX_GRAVITY_LEFT, cdrom1_handler);
251   BX_GUI_THIS set_tooltip(BX_GUI_THIS cdrom1_hbar_id, "Change first CDROM media");
252 
253   // Mouse button
254   if (SIM->get_param_bool(BXPN_MOUSE_ENABLED)->get())
255     BX_GUI_THIS mouse_hbar_id = headerbar_bitmap(BX_GUI_THIS mouse_bmap_id,
256                           BX_GRAVITY_LEFT, toggle_mouse_enable);
257   else
258     BX_GUI_THIS mouse_hbar_id = headerbar_bitmap(BX_GUI_THIS nomouse_bmap_id,
259                           BX_GRAVITY_LEFT, toggle_mouse_enable);
260   BX_GUI_THIS set_tooltip(BX_GUI_THIS mouse_hbar_id, "Enable mouse capture");
261 
262   // These are the buttons on the right side.  They are created in order
263   // of right to left.
264 
265   // Power button
266   BX_GUI_THIS power_hbar_id = headerbar_bitmap(BX_GUI_THIS power_bmap_id,
267                           BX_GRAVITY_RIGHT, power_handler);
268   BX_GUI_THIS set_tooltip(BX_GUI_THIS power_hbar_id, "Turn power off");
269   // Save/Restore Button
270   BX_GUI_THIS save_restore_hbar_id = headerbar_bitmap(BX_GUI_THIS save_restore_bmap_id,
271                           BX_GRAVITY_RIGHT, save_restore_handler);
272   BX_GUI_THIS set_tooltip(BX_GUI_THIS save_restore_hbar_id, "Save simulation state");
273   // Reset button
274   BX_GUI_THIS reset_hbar_id = headerbar_bitmap(BX_GUI_THIS reset_bmap_id,
275                           BX_GRAVITY_RIGHT, reset_handler);
276   BX_GUI_THIS set_tooltip(BX_GUI_THIS reset_hbar_id, "Reset the system");
277   // Configure button
278   BX_GUI_THIS config_hbar_id = headerbar_bitmap(BX_GUI_THIS config_bmap_id,
279                           BX_GRAVITY_RIGHT, config_handler);
280   BX_GUI_THIS set_tooltip(BX_GUI_THIS config_hbar_id, "Runtime config dialog");
281   // Snapshot button
282   BX_GUI_THIS snapshot_hbar_id = headerbar_bitmap(BX_GUI_THIS snapshot_bmap_id,
283                           BX_GRAVITY_RIGHT, snapshot_handler);
284   BX_GUI_THIS set_tooltip(BX_GUI_THIS snapshot_hbar_id, "Save snapshot of the Bochs screen");
285   // Paste button
286   BX_GUI_THIS paste_hbar_id = headerbar_bitmap(BX_GUI_THIS paste_bmap_id,
287                           BX_GRAVITY_RIGHT, paste_handler);
288   BX_GUI_THIS set_tooltip(BX_GUI_THIS paste_hbar_id, "Paste clipboard text as emulated keystrokes");
289   // Copy button
290   BX_GUI_THIS copy_hbar_id = headerbar_bitmap(BX_GUI_THIS copy_bmap_id,
291                           BX_GRAVITY_RIGHT, copy_handler);
292   BX_GUI_THIS set_tooltip(BX_GUI_THIS copy_hbar_id, "Copy text mode screen to the clipboard");
293   // User button
294   BX_GUI_THIS user_hbar_id = headerbar_bitmap(BX_GUI_THIS user_bmap_id,
295                           BX_GRAVITY_RIGHT, userbutton_handler);
296   BX_GUI_THIS set_tooltip(BX_GUI_THIS user_hbar_id, "Send keyboard shortcut");
297 
298   if (!parse_user_shortcut(SIM->get_param_string(BXPN_USER_SHORTCUT)->getptr())) {
299     SIM->get_param_string(BXPN_USER_SHORTCUT)->set("none");
300   }
301 
302   BX_GUI_THIS charmap_updated = 0;
303 
304   if (!BX_GUI_THIS new_gfx_api && (BX_GUI_THIS framebuffer == NULL)) {
305     BX_GUI_THIS framebuffer = new Bit8u[max_xres * max_yres * 4];
306   }
307   show_headerbar();
308 
309   // register timer for status bar LEDs
310   if (BX_GUI_THIS led_timer_index == BX_NULL_TIMER_HANDLE) {
311     BX_GUI_THIS led_timer_index =
312       bx_virt_timer.register_timer(this, led_timer_handler, 100000, 1, 1, 1,
313                                    "status bar LEDs");
314   }
315 }
316 
cleanup(void)317 void bx_gui_c::cleanup(void)
318 {
319   statusitem_count = 0;
320 }
321 
update_drive_status_buttons(void)322 void bx_gui_c::update_drive_status_buttons(void)
323 {
324   BX_GUI_THIS floppyA_status = (SIM->get_param_enum(BXPN_FLOPPYA_STATUS)->get() == BX_INSERTED);
325   BX_GUI_THIS floppyB_status = (SIM->get_param_enum(BXPN_FLOPPYB_STATUS)->get() == BX_INSERTED);
326   bx_param_c *cdrom = SIM->get_first_cdrom();
327   if (cdrom != NULL) {
328     BX_GUI_THIS cdrom1_status = SIM->get_param_enum("status", cdrom)->get();
329   } else {
330     BX_GUI_THIS cdrom1_status = BX_EJECTED;
331   }
332   if (BX_GUI_THIS floppyA_status)
333     replace_bitmap(BX_GUI_THIS floppyA_hbar_id, BX_GUI_THIS floppyA_bmap_id);
334   else {
335 #if BX_WITH_MACOS
336     // If we are using the Mac floppy driver, eject the disk
337     // from the floppy drive.  This doesn't work in MacOS X.
338     if (!strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
339       DiskEject(1);
340 #endif
341     replace_bitmap(BX_GUI_THIS floppyA_hbar_id, BX_GUI_THIS floppyA_eject_bmap_id);
342   }
343   if (BX_GUI_THIS floppyB_status)
344     replace_bitmap(BX_GUI_THIS floppyB_hbar_id, BX_GUI_THIS floppyB_bmap_id);
345   else {
346 #if BX_WITH_MACOS
347     // If we are using the Mac floppy driver, eject the disk
348     // from the floppy drive.  This doesn't work in MacOS X.
349     if (!strcmp(SIM->get_param_string(BXPN_FLOPPYB_PATH)->getptr(), SuperDrive))
350       DiskEject(1);
351 #endif
352     replace_bitmap(BX_GUI_THIS floppyB_hbar_id, BX_GUI_THIS floppyB_eject_bmap_id);
353   }
354   if (BX_GUI_THIS cdrom1_status)
355     replace_bitmap(BX_GUI_THIS cdrom1_hbar_id, BX_GUI_THIS cdrom1_bmap_id);
356   else {
357     replace_bitmap(BX_GUI_THIS cdrom1_hbar_id, BX_GUI_THIS cdrom1_eject_bmap_id);
358   }
359 }
360 
floppyA_handler(void)361 void bx_gui_c::floppyA_handler(void)
362 {
363   int ret = 1;
364 
365   if (SIM->get_param_enum(BXPN_FLOPPYA_DEVTYPE)->get() == BX_FDD_NONE)
366     return; // no primary floppy device present
367   if (!BX_GUI_THIS fullscreen_mode &&
368       (BX_GUI_THIS dialog_caps & BX_GUI_DLG_FLOPPY)) {
369     // instead of just toggling the status, bring up a dialog asking what disk
370     // image you want to switch to.
371     ret = SIM->ask_param(BXPN_FLOPPYA);
372   } else {
373     SIM->get_param_enum(BXPN_FLOPPYA_STATUS)->set(!BX_GUI_THIS floppyA_status);
374   }
375   if (ret > 0) {
376     SIM->update_runtime_options();
377   }
378 }
379 
floppyB_handler(void)380 void bx_gui_c::floppyB_handler(void)
381 {
382   int ret = 1;
383 
384   if (SIM->get_param_enum(BXPN_FLOPPYB_DEVTYPE)->get() == BX_FDD_NONE)
385     return; // no secondary floppy device present
386   if (!BX_GUI_THIS fullscreen_mode &&
387       (BX_GUI_THIS dialog_caps & BX_GUI_DLG_FLOPPY)) {
388     // instead of just toggling the status, bring up a dialog asking what disk
389     // image you want to switch to.
390     ret = SIM->ask_param(BXPN_FLOPPYB);
391   } else {
392     SIM->get_param_enum(BXPN_FLOPPYB_STATUS)->set(!BX_GUI_THIS floppyB_status);
393   }
394   if (ret > 0) {
395     SIM->update_runtime_options();
396   }
397 }
398 
cdrom1_handler(void)399 void bx_gui_c::cdrom1_handler(void)
400 {
401   int ret = 1;
402   bx_param_c *cdrom = SIM->get_first_cdrom();
403 
404   if (cdrom == NULL)
405     return;  // no cdrom found
406   if (BX_GUI_THIS dialog_caps & BX_GUI_DLG_CDROM) {
407     // instead of just toggling the status, bring up a dialog asking what disk
408     // image you want to switch to.
409     // This code handles the first cdrom only. The cdrom drives #2, #3 and
410     // #4 are handled in the runtime configuaration.
411     ret = SIM->ask_param(cdrom);
412   } else {
413     SIM->get_param_enum("status", cdrom)->set(!BX_GUI_THIS cdrom1_status);
414   }
415   if (ret > 0) {
416     SIM->update_runtime_options();
417   }
418 }
419 
reset_handler(void)420 void bx_gui_c::reset_handler(void)
421 {
422   BX_INFO(("system RESET callback"));
423   bx_pc_system.Reset(BX_RESET_HARDWARE);
424 }
425 
power_handler(void)426 void bx_gui_c::power_handler(void)
427 {
428 #ifdef BX_GUI_CONFIRM_QUIT
429   if (!SIM->ask_yes_no("Quit Bochs", "Are you sure ?", 0))
430     return;
431 #endif
432   // the user pressed power button, so there's no doubt they want bochs
433   // to quit.  Change panics to fatal for the GUI and then do a panic.
434   bx_user_quit = 1;
435   BX_FATAL(("POWER button turned off."));
436   // shouldn't reach this point, but if you do, QUIT!!!
437   fprintf (stderr, "Bochs is exiting because you pressed the power button.\n");
438   BX_EXIT(1);
439 }
440 
make_text_snapshot(char ** snapshot,Bit32u * length)441 void bx_gui_c::make_text_snapshot(char **snapshot, Bit32u *length)
442 {
443   Bit8u* raw_snap = NULL;
444   char *clean_snap;
445   unsigned line_addr, txt_addr, txHeight, txWidth;
446 
447   DEV_vga_get_text_snapshot(&raw_snap, &txHeight, &txWidth);
448   clean_snap = new char[txHeight*(txWidth+2)+1];
449   txt_addr = 0;
450   for (unsigned i=0; i<txHeight; i++) {
451     line_addr = i * txWidth * 2;
452     for (unsigned j=0; j<(txWidth*2); j+=2) {
453       if (!raw_snap[line_addr+j])
454         raw_snap[line_addr+j] = 0x20;
455       clean_snap[txt_addr++] = raw_snap[line_addr+j];
456     }
457     while ((txt_addr > 0) && (clean_snap[txt_addr-1] == ' ')) txt_addr--;
458 #ifdef WIN32
459     clean_snap[txt_addr++] = 13;
460 #endif
461     clean_snap[txt_addr++] = 10;
462   }
463   clean_snap[txt_addr] = 0;
464   *snapshot = clean_snap;
465   *length = txt_addr;
466 }
467 
set_snapshot_mode(bool mode)468 Bit32u bx_gui_c::set_snapshot_mode(bool mode)
469 {
470   unsigned pixel_bytes, bufsize;
471 
472   BX_GUI_THIS snapshot_mode = mode;
473   if (mode) {
474     pixel_bytes = ((BX_GUI_THIS guest_bpp + 1) >> 3);
475     bufsize = BX_GUI_THIS guest_xres * BX_GUI_THIS guest_yres * pixel_bytes;
476     BX_GUI_THIS snapshot_buffer = new Bit8u[bufsize];
477     if (BX_GUI_THIS snapshot_buffer != NULL) {
478       memset(BX_GUI_THIS snapshot_buffer, 0, bufsize);
479       DEV_vga_refresh(1);
480       return bufsize;
481     }
482   } else {
483     if (BX_GUI_THIS snapshot_buffer != NULL) {
484       delete [] BX_GUI_THIS snapshot_buffer;
485       BX_GUI_THIS snapshot_buffer = NULL;
486       DEV_vga_redraw_area(0, 0, BX_GUI_THIS guest_xres, BX_GUI_THIS guest_yres);
487     }
488   }
489   return 0;
490 }
491 
492 // create a text snapshot and copy to the system clipboard.  On guis that
493 // we haven't figured out how to support yet, dump to a file instead.
copy_handler(void)494 void bx_gui_c::copy_handler(void)
495 {
496   Bit32u len;
497   char *text_snapshot;
498 
499   if (BX_GUI_THIS guest_textmode) {
500     make_text_snapshot(&text_snapshot, &len);
501     if (!BX_GUI_THIS set_clipboard_text(text_snapshot, len)) {
502       // platform specific code failed, use portable code instead
503       FILE *fp = fopen("copy.txt", "w");
504       if (fp != NULL) {
505         fwrite(text_snapshot, 1, len, fp);
506         fclose(fp);
507       }
508     }
509     delete [] text_snapshot;
510   } else {
511     BX_ERROR(("copy button failed, graphics mode not implemented"));
512   }
513 }
514 
515 #define BX_SNAPSHOT_TXT 0
516 #define BX_SNAPSHOT_BMP 1
517 
518 // create a text snapshot and dump it to a file
snapshot_handler(void)519 void bx_gui_c::snapshot_handler(void)
520 {
521   int fd, i, j, pitch;
522   Bit8u *snapshot_ptr = NULL;
523   Bit8u *row_buffer, *pixel_ptr, *row_ptr;
524   Bit8u bmp_header[54], iBits, b1, b2;
525   Bit32u ilen, len, rlen;
526   char filename[BX_PATHNAME_LEN], msg[80], *ext;
527   Bit8u snap_fmt;
528 
529   if (BX_GUI_THIS guest_textmode) {
530     strcpy(filename, "snapshot.txt");
531     snap_fmt = BX_SNAPSHOT_TXT;
532   } else {
533     strcpy(filename, "snapshot.bmp");
534     snap_fmt = BX_SNAPSHOT_BMP;
535   }
536   if (!BX_GUI_THIS fullscreen_mode &&
537       (BX_GUI_THIS dialog_caps & BX_GUI_DLG_SNAPSHOT)) {
538     int ret = SIM->ask_filename(filename, sizeof(filename),
539                                 "Save snapshot as...", filename,
540                                 bx_param_string_c::SAVE_FILE_DIALOG);
541     if (ret < 0) { // cancelled
542       return;
543     }
544     ext = strrchr(filename, '.');
545     if (ext == NULL) {
546       SIM->message_box("ERROR", "Unknown snapshot file format");
547       return;
548     } else {
549       ext++;
550       if (BX_GUI_THIS guest_textmode && !strcmp(ext, "txt")) {
551         snap_fmt = BX_SNAPSHOT_TXT;
552       } else if (!strcmp(ext, "bmp")) {
553         snap_fmt = BX_SNAPSHOT_BMP;
554       } else {
555         sprintf(msg, "Unsupported snapshot file format '%s'", ext);
556         SIM->message_box("ERROR", msg);
557         return;
558       }
559     }
560   }
561   if (snap_fmt == BX_SNAPSHOT_BMP) {
562     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC
563 #ifdef O_BINARY
564               | O_BINARY
565 #endif
566               , S_IRUSR | S_IWUSR
567               );
568     if (fd < 0) {
569       SIM->message_box("ERROR", "snapshot button failed: cannot create BMP file");
570       return;
571     }
572     ilen =  BX_GUI_THIS set_snapshot_mode(1);
573     if (ilen > 0) {
574       BX_INFO(("GFX snapshot: %u x %u x %u bpp (%u bytes)", BX_GUI_THIS guest_xres,
575                BX_GUI_THIS guest_yres, BX_GUI_THIS guest_bpp, ilen));
576     } else {
577       close(fd);
578       SIM->message_box("ERROR", "snapshot button failed: cannot allocate memory");
579       return;
580     }
581     iBits = (BX_GUI_THIS guest_bpp == 8) ? 8 : 24;
582     rlen = (BX_GUI_THIS guest_xres * (iBits >> 3) + 3) & ~3;
583     len = rlen * BX_GUI_THIS guest_yres + 54;
584     if (BX_GUI_THIS guest_bpp == 8) {
585       len += (256 * 4);
586     }
587     memset(bmp_header, 0, 54);
588     bmp_header[0] = 0x42;
589     bmp_header[1] = 0x4d;
590     bmp_header[2] = len & 0xff;
591     bmp_header[3] = (len >> 8) & 0xff;
592     bmp_header[4] = (len >> 16) & 0xff;
593     bmp_header[5] = (len >> 24) & 0xff;
594     bmp_header[10] = 54;
595     if (BX_GUI_THIS guest_bpp == 8) {
596       bmp_header[11] = 4;
597     }
598     bmp_header[14] = 40;
599     bmp_header[18] = BX_GUI_THIS guest_xres & 0xff;
600     bmp_header[19] = (BX_GUI_THIS guest_xres >> 8) & 0xff;
601     bmp_header[22] = BX_GUI_THIS guest_yres & 0xff;
602     bmp_header[23] = (BX_GUI_THIS guest_yres >> 8) & 0xff;
603     bmp_header[26] = 1;
604     bmp_header[28] = iBits;
605     write(fd, bmp_header, 54);
606     if (BX_GUI_THIS guest_bpp == 8) {
607       write(fd, BX_GUI_THIS palette, 256 * 4);
608     }
609     pitch = BX_GUI_THIS guest_xres * ((BX_GUI_THIS guest_bpp + 1) >> 3);
610     row_buffer = new Bit8u[rlen];
611     row_ptr = BX_GUI_THIS snapshot_buffer + ((BX_GUI_THIS guest_yres - 1) * pitch);
612     for (i = BX_GUI_THIS guest_yres; i > 0; i--) {
613       memset(row_buffer, 0, rlen);
614       if ((BX_GUI_THIS guest_bpp == 8) || (BX_GUI_THIS guest_bpp == 24)) {
615         memcpy(row_buffer, row_ptr, pitch);
616       } else if ((BX_GUI_THIS guest_bpp == 15) || (BX_GUI_THIS guest_bpp == 16)) {
617         pixel_ptr = row_ptr;
618         for (j = 0; j < (int)(BX_GUI_THIS guest_xres * 3); j+=3) {
619           b1 = *(pixel_ptr++);
620           b2 = *(pixel_ptr++);
621           *(row_buffer+j)   = (b1 << 3);
622           if (BX_GUI_THIS guest_bpp == 15) {
623             *(row_buffer+j+1) = ((b1 & 0xe0) >> 2) | (b2 << 6);
624             *(row_buffer+j+2) = (b2 & 0x7c) << 1;
625           } else {
626             *(row_buffer+j+1) = ((b1 & 0xe0) >> 3) | (b2 << 5);
627             *(row_buffer+j+2) = (b2 & 0xf8);
628           }
629         }
630       } else if (BX_GUI_THIS guest_bpp == 32) {
631         pixel_ptr = row_ptr;
632         for (j = 0; j < (int)(BX_GUI_THIS guest_xres * 3); j+=3) {
633           *(row_buffer+j)   = *(pixel_ptr++);
634           *(row_buffer+j+1) = *(pixel_ptr++);
635           *(row_buffer+j+2) = *(pixel_ptr++);
636           pixel_ptr++;
637         }
638       }
639       write(fd, row_buffer, rlen);
640       row_ptr -= pitch;
641     }
642     delete [] row_buffer;
643     close(fd);
644     BX_GUI_THIS set_snapshot_mode(0);
645   } else {
646     make_text_snapshot((char**)&snapshot_ptr, &len);
647     FILE *fp = fopen(filename, "wb");
648     if (fp != NULL) {
649       fwrite(snapshot_ptr, 1, len, fp);
650       fclose(fp);
651     } else {
652       SIM->message_box("ERROR", "snapshot button failed: cannot create text file");
653     }
654     delete [] snapshot_ptr;
655   }
656 }
657 
658 // Read ASCII chars from the system clipboard and paste them into bochs.
659 // Note that paste cannot work with the key mapping tables loaded.
paste_handler(void)660 void bx_gui_c::paste_handler(void)
661 {
662   Bit32s nbytes;
663   Bit8u *bytes;
664   if (!bx_keymap.isKeymapLoaded ()) {
665     BX_ERROR(("keyboard_mapping disabled, so paste cannot work"));
666     return;
667   }
668   if (!BX_GUI_THIS get_clipboard_text(&bytes, &nbytes)) {
669     BX_ERROR (("paste not implemented on this platform"));
670     return;
671   }
672   BX_INFO (("pasting %d bytes", nbytes));
673   DEV_kbd_paste_bytes (bytes, nbytes);
674 }
675 
config_handler(void)676 void bx_gui_c::config_handler(void)
677 {
678   if (BX_GUI_THIS dialog_caps & BX_GUI_DLG_RUNTIME) {
679     SIM->configuration_interface(NULL, CI_RUNTIME_CONFIG);
680   }
681 }
682 
toggle_mouse_enable(void)683 void bx_gui_c::toggle_mouse_enable(void)
684 {
685   int old = SIM->get_param_bool(BXPN_MOUSE_ENABLED)->get();
686   BX_DEBUG (("toggle mouse_enabled, now %d", !old));
687   SIM->get_param_bool(BXPN_MOUSE_ENABLED)->set(!old);
688 }
689 
mouse_toggle_check(Bit32u key,bool pressed)690 bool bx_gui_c::mouse_toggle_check(Bit32u key, bool pressed)
691 {
692   Bit32u newstate;
693   bool toggle = 0;
694 
695   if (console_running())
696     return 0;
697   newstate = toggle_keystate;
698   if (pressed) {
699     newstate |= key;
700     if (newstate == toggle_keystate) return 0;
701     switch (toggle_method) {
702       case BX_MOUSE_TOGGLE_CTRL_MB:
703         toggle = (newstate & BX_GUI_MT_CTRL_MB) == BX_GUI_MT_CTRL_MB;
704         if (!toggle) {
705           toggle = (newstate & BX_GUI_MT_CTRL_LRB) == BX_GUI_MT_CTRL_LRB;
706         }
707         break;
708       case BX_MOUSE_TOGGLE_CTRL_F10:
709         toggle = (newstate & BX_GUI_MT_CTRL_F10) == BX_GUI_MT_CTRL_F10;
710         break;
711       case BX_MOUSE_TOGGLE_CTRL_ALT:
712         toggle = (newstate & BX_GUI_MT_CTRL_ALT) == BX_GUI_MT_CTRL_ALT;
713         break;
714       case BX_MOUSE_TOGGLE_F12:
715         toggle = (newstate == BX_GUI_MT_F12);
716         break;
717     }
718     toggle_keystate = newstate;
719   } else {
720     toggle_keystate &= ~key;
721   }
722   return toggle;
723 }
724 
get_toggle_info(void)725 const char* bx_gui_c::get_toggle_info(void)
726 {
727   return mouse_toggle_text;
728 }
729 
get_modifier_keys(void)730 Bit8u bx_gui_c::get_modifier_keys(void)
731 {
732   if ((keymodstate & BX_MOD_KEY_CAPS) > 0) {
733     return ((keymodstate & ~BX_MOD_KEY_CAPS) | BX_MOD_KEY_SHIFT);
734   } else {
735     return keymodstate;
736   }
737 }
738 
set_modifier_keys(Bit8u modifier,bool pressed)739 Bit8u bx_gui_c::set_modifier_keys(Bit8u modifier, bool pressed)
740 {
741   Bit8u newstate = keymodstate, changestate = 0;
742 
743   if (modifier == BX_MOD_KEY_CAPS) {
744     if (pressed) {
745       newstate ^= modifier;
746     }
747   } else {
748     if (pressed) {
749       newstate |= modifier;
750     } else {
751       newstate &= ~modifier;
752     }
753   }
754   changestate = keymodstate ^ newstate;
755   keymodstate = newstate;
756   return changestate;
757 }
758 
parse_user_shortcut(const char * val)759 bool bx_gui_c::parse_user_shortcut(const char *val)
760 {
761   char *ptr, shortcut_tmp[512];
762   Bit32u symbol, new_shortcut[4];
763   int i, len = 0;
764 
765   user_shortcut_error = 0;
766   if ((strlen(val) == 0) || !strcmp(val, "none")) {
767     user_shortcut_len = 0;
768     return 1;
769   } else {
770     len = 0;
771     strcpy(shortcut_tmp, val);
772     ptr = strtok(shortcut_tmp, "-");
773     while (ptr) {
774       symbol = get_user_key(ptr);
775       if (symbol == BX_KEY_UNKNOWN) {
776         BX_ERROR(("Unknown key symbol '%s' ignored", ptr));
777         user_shortcut_error = 1;
778         return 0;
779       }
780       if (len < 3) {
781         new_shortcut[len++] = symbol;
782         ptr = strtok(NULL, "-");
783       } else {
784         BX_ERROR(("Ignoring extra key symbol '%s'", ptr));
785         break;
786       }
787     }
788     for (i = 0; i < len; i++) {
789       user_shortcut[i] = new_shortcut[i];
790     }
791     user_shortcut_len = len;
792     return 1;
793   }
794 }
795 
userbutton_handler(void)796 void bx_gui_c::userbutton_handler(void)
797 {
798   int i, ret = 1;
799 
800   if (!BX_GUI_THIS fullscreen_mode &&
801       (BX_GUI_THIS dialog_caps & BX_GUI_DLG_USER)) {
802     ret = SIM->ask_param(BXPN_USER_SHORTCUT);
803   }
804   if (BX_GUI_THIS user_shortcut_error) {
805     SIM->message_box("ERROR", "Ignoring invalid user shortcut");
806     return;
807   }
808   if ((ret > 0) && (BX_GUI_THIS user_shortcut_len > 0)) {
809     i = 0;
810     while (i < BX_GUI_THIS user_shortcut_len) {
811       DEV_kbd_gen_scancode(BX_GUI_THIS user_shortcut[i++]);
812     }
813     i--;
814     while (i >= 0) {
815       DEV_kbd_gen_scancode(BX_GUI_THIS user_shortcut[i--] | BX_KEY_RELEASED);
816     }
817   }
818 }
819 
save_restore_handler(void)820 void bx_gui_c::save_restore_handler(void)
821 {
822   int ret;
823   char sr_path[BX_PATHNAME_LEN];
824 
825   if (BX_GUI_THIS dialog_caps & BX_GUI_DLG_SAVE_RESTORE) {
826     BX_GUI_THIS set_display_mode(DISP_MODE_CONFIG);
827     sr_path[0] = 0;
828     ret = SIM->ask_filename(sr_path, sizeof(sr_path),
829                             "Save Bochs state to folder...", "none",
830                             bx_param_string_c::SELECT_FOLDER_DLG);
831     if ((ret >= 0) && (strcmp(sr_path, "none"))) {
832       if (SIM->save_state(sr_path)) {
833         if (!SIM->ask_yes_no("WARNING",
834               "The state of cpu, memory, devices and hard drive images is saved now.\n"
835               "It is possible to continue, but when using the restore function in a\n"
836               "new Bochs session, all changes after this checkpoint will be lost.\n\n"
837               "Do you want to continue?", 0)) {
838           power_handler();
839         }
840       } else {
841         SIM->message_box("ERROR", "Failed to save state");
842       }
843     }
844     BX_GUI_THIS set_display_mode(DISP_MODE_SIM);
845   }
846 }
847 
marklog_handler(void)848 void bx_gui_c::marklog_handler(void)
849 {
850   BX_INFO(("### MARKER #%u", BX_GUI_THIS marker_count++));
851 }
852 
headerbar_click(int x)853 void bx_gui_c::headerbar_click(int x)
854 {
855   int xorigin;
856 
857   for (unsigned i=0; i<bx_headerbar_entries; i++) {
858     if (bx_headerbar_entry[i].alignment == BX_GRAVITY_LEFT)
859       xorigin = bx_headerbar_entry[i].xorigin;
860     else
861       xorigin = guest_xres - bx_headerbar_entry[i].xorigin;
862     if ((x>=xorigin) && (x<(xorigin+int(bx_headerbar_entry[i].xdim)))) {
863       if (console_running() && (i != power_hbar_id))
864         return;
865       bx_headerbar_entry[i].f();
866       return;
867     }
868   }
869 }
870 
mouse_enabled_changed(bool val)871 void bx_gui_c::mouse_enabled_changed(bool val)
872 {
873   // This is only called when SIM->get_init_done is 1.  Note that VAL
874   // is the new value of mouse_enabled, which may not match the old
875   // value which is still in SIM->get_param_bool(BXPN_MOUSE_ENABLED)->get().
876   BX_DEBUG (("replacing the mouse bitmaps"));
877   if (val)
878     BX_GUI_THIS replace_bitmap(BX_GUI_THIS mouse_hbar_id, BX_GUI_THIS mouse_bmap_id);
879   else
880     BX_GUI_THIS replace_bitmap(BX_GUI_THIS mouse_hbar_id, BX_GUI_THIS nomouse_bmap_id);
881   // give the GUI a chance to respond to the event.  Most guis will hide
882   // the native mouse cursor and do something to trap the mouse inside the
883   // bochs VGA display window.
884   BX_GUI_THIS mouse_enabled_changed_specific (val);
885 }
886 
init_signal_handlers()887 void bx_gui_c::init_signal_handlers()
888 {
889 #if BX_GUI_SIGHANDLER
890   if (bx_gui_sighandler)
891   {
892     Bit32u mask = bx_gui->get_sighandler_mask ();
893     for (Bit32u sig=0; sig<32; sig++)
894     {
895       if (mask & (1<<sig))
896         signal (sig, bx_signal_handler);
897     }
898   }
899 #endif
900 }
901 
set_text_charmap(Bit8u * fbuffer)902 void bx_gui_c::set_text_charmap(Bit8u *fbuffer)
903 {
904   memcpy(& BX_GUI_THIS vga_charmap, fbuffer, 0x2000);
905   for (unsigned i=0; i<256; i++) BX_GUI_THIS char_changed[i] = 1;
906   BX_GUI_THIS charmap_updated = 1;
907 }
908 
set_text_charbyte(Bit16u address,Bit8u data)909 void bx_gui_c::set_text_charbyte(Bit16u address, Bit8u data)
910 {
911   BX_GUI_THIS vga_charmap[address] = data;
912   BX_GUI_THIS char_changed[address >> 5] = 1;
913   BX_GUI_THIS charmap_updated = 1;
914 }
915 
beep_on(float frequency)916 void bx_gui_c::beep_on(float frequency)
917 {
918   BX_DEBUG(("GUI Beep ON (frequency=%.2f)", frequency));
919 }
920 
beep_off()921 void bx_gui_c::beep_off()
922 {
923   BX_DEBUG(("GUI Beep OFF"));
924 }
925 
register_statusitem(const char * text,bool auto_off)926 int bx_gui_c::register_statusitem(const char *text, bool auto_off)
927 {
928   unsigned id = statusitem_count;
929 
930   for (unsigned i = 0; i < statusitem_count; i++) {
931     if (!statusitem[i].in_use) {
932       id = i;
933       break;
934     }
935   }
936   if (id == statusitem_count) {
937     if (statusitem_count == BX_MAX_STATUSITEMS) {
938       return -1;
939     } else {
940       statusitem_count++;
941     }
942   }
943   statusitem[id].in_use = 1;
944   strncpy(statusitem[id].text, text, 8);
945   statusitem[id].text[7] = 0;
946   statusitem[id].auto_off = auto_off;
947   statusitem[id].counter = 0;
948   statusitem[id].active = 0;
949   statusitem[id].mode = 0;
950   statusbar_setitem_specific(id, 0, 0);
951   return id;
952 }
953 
unregister_statusitem(int id)954 void bx_gui_c::unregister_statusitem(int id)
955 {
956   if ((id >= 0) && (id < (int)statusitem_count)) {
957     strcpy(statusitem[id].text, "      ");
958     statusbar_setitem_specific(id, 0, 0);
959     if (id == (int)(statusitem_count - 1)) {
960       statusitem_count--;
961     } else {
962       statusitem[id].in_use = 0;
963     }
964   }
965 }
966 
statusbar_setitem(int element,bool active,bool w)967 void bx_gui_c::statusbar_setitem(int element, bool active, bool w)
968 {
969   if (element < 0) {
970     for (unsigned i = 0; i < statusitem_count; i++) {
971       statusbar_setitem_specific(i, 0, 0);
972     }
973   } else if ((unsigned)element < statusitem_count) {
974     if ((active != statusitem[element].active) ||
975         (w != statusitem[element].mode)) {
976       statusbar_setitem_specific(element, active, w);
977       statusitem[element].active = active;
978       statusitem[element].mode = w;
979     }
980     if (active && statusitem[element].auto_off) {
981       statusitem[element].counter = 5;
982     }
983   }
984 }
985 
led_timer_handler(void * this_ptr)986 void bx_gui_c::led_timer_handler(void *this_ptr)
987 {
988   bx_gui_c *class_ptr = (bx_gui_c *) this_ptr;
989   class_ptr->led_timer();
990 }
991 
led_timer()992 void bx_gui_c::led_timer()
993 {
994   for (unsigned i = 0; i < statusitem_count; i++) {
995     if (statusitem[i].auto_off) {
996       if (statusitem[i].counter > 0) {
997         if (!(--statusitem[i].counter)) {
998           statusbar_setitem(i, 0);
999         }
1000       }
1001     }
1002   }
1003 }
1004 
get_capabilities(Bit16u * xres,Bit16u * yres,Bit16u * bpp)1005 void bx_gui_c::get_capabilities(Bit16u *xres, Bit16u *yres, Bit16u *bpp)
1006 {
1007   *xres = 1024;
1008   *yres = 768;
1009   *bpp = 32;
1010 }
1011 
palette_change_common(Bit8u index,Bit8u red,Bit8u green,Bit8u blue)1012 bool bx_gui_c::palette_change_common(Bit8u index, Bit8u red, Bit8u green, Bit8u blue)
1013 {
1014   BX_GUI_THIS palette[index].red = red;
1015   BX_GUI_THIS palette[index].green = green;
1016   BX_GUI_THIS palette[index].blue = blue;
1017   return palette_change(index, red, green, blue);
1018 }
1019 
show_ips(Bit32u ips_count)1020 void bx_gui_c::show_ips(Bit32u ips_count)
1021 {
1022 #if BX_SHOW_IPS
1023   BX_INFO(("ips = %3.3fM", ips_count / 1000000.0));
1024 #endif
1025 }
1026 
get_mouse_headerbar_id()1027 Bit8u bx_gui_c::get_mouse_headerbar_id()
1028 {
1029   return BX_GUI_THIS mouse_hbar_id;
1030 }
1031 
1032 #if BX_DEBUGGER && BX_DEBUGGER_GUI
init_debug_dialog()1033 void bx_gui_c::init_debug_dialog()
1034 {
1035   extern void InitDebugDialog();
1036   InitDebugDialog();
1037 }
1038 
close_debug_dialog()1039 void bx_gui_c::close_debug_dialog()
1040 {
1041   extern void CloseDebugDialog();
1042   CloseDebugDialog();
1043 }
1044 #endif
1045 
1046 // new graphics API (compatibility code)
1047 
graphics_tile_info(bx_svga_tileinfo_t * info)1048 bx_svga_tileinfo_t *bx_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info)
1049 {
1050   BX_GUI_THIS host_pitch = BX_GUI_THIS host_xres * ((BX_GUI_THIS host_bpp + 1) >> 3);
1051 
1052   info->bpp = BX_GUI_THIS host_bpp;
1053   info->pitch = BX_GUI_THIS host_pitch;
1054   switch (info->bpp) {
1055     case 15:
1056       info->red_shift = 15;
1057       info->green_shift = 10;
1058       info->blue_shift = 5;
1059       info->red_mask = 0x7c00;
1060       info->green_mask = 0x03e0;
1061       info->blue_mask = 0x001f;
1062       break;
1063     case 16:
1064       info->red_shift = 16;
1065       info->green_shift = 11;
1066       info->blue_shift = 5;
1067       info->red_mask = 0xf800;
1068       info->green_mask = 0x07e0;
1069       info->blue_mask = 0x001f;
1070       break;
1071     case 24:
1072     case 32:
1073       info->red_shift = 24;
1074       info->green_shift = 16;
1075       info->blue_shift = 8;
1076       info->red_mask = 0xff0000;
1077       info->green_mask = 0x00ff00;
1078       info->blue_mask = 0x0000ff;
1079       break;
1080   }
1081   info->is_indexed = (BX_GUI_THIS host_bpp == 8);
1082 #ifdef BX_LITTLE_ENDIAN
1083   info->is_little_endian = 1;
1084 #else
1085   info->is_little_endian = 0;
1086 #endif
1087 
1088   return info;
1089 }
1090 
graphics_tile_get(unsigned x0,unsigned y0,unsigned * w,unsigned * h)1091 Bit8u *bx_gui_c::graphics_tile_get(unsigned x0, unsigned y0,
1092                             unsigned *w, unsigned *h)
1093 {
1094   if (x0+BX_GUI_THIS x_tilesize > BX_GUI_THIS host_xres) {
1095     *w = BX_GUI_THIS host_xres - x0;
1096   } else {
1097     *w = BX_GUI_THIS x_tilesize;
1098   }
1099 
1100   if (y0+BX_GUI_THIS y_tilesize > BX_GUI_THIS host_yres) {
1101     *h = BX_GUI_THIS host_yres - y0;
1102   } else {
1103     *h = BX_GUI_THIS y_tilesize;
1104   }
1105 
1106   return (Bit8u *)framebuffer + y0 * BX_GUI_THIS host_pitch +
1107                   x0 * ((BX_GUI_THIS host_bpp + 1) >> 3);
1108 }
1109 
graphics_tile_update_in_place(unsigned x0,unsigned y0,unsigned w,unsigned h)1110 void bx_gui_c::graphics_tile_update_in_place(unsigned x0, unsigned y0,
1111                                         unsigned w, unsigned h)
1112 {
1113   Bit8u *tile;
1114   Bit8u *tile_ptr, *fb_ptr;
1115   Bit16u xc, yc, fb_pitch, tile_pitch;
1116   Bit8u r, diffx, diffy;
1117 
1118   tile = new Bit8u[BX_GUI_THIS x_tilesize * BX_GUI_THIS y_tilesize * 4];
1119   diffx = (x0 % BX_GUI_THIS x_tilesize);
1120   diffy = (y0 % BX_GUI_THIS y_tilesize);
1121   if (diffx > 0) {
1122     x0 -= diffx;
1123     w += diffx;
1124   }
1125   if (diffy > 0) {
1126     y0 -= diffy;
1127     h += diffy;
1128   }
1129   fb_pitch = BX_GUI_THIS host_pitch;
1130   tile_pitch = BX_GUI_THIS x_tilesize * ((BX_GUI_THIS host_bpp + 1) >> 3);
1131   for (yc=y0; yc<(y0+h); yc+=BX_GUI_THIS y_tilesize) {
1132     for (xc=x0; xc<(x0+w); xc+=BX_GUI_THIS x_tilesize) {
1133       fb_ptr = BX_GUI_THIS framebuffer + (yc * fb_pitch + xc * ((BX_GUI_THIS host_bpp + 1) >> 3));
1134       tile_ptr = &tile[0];
1135       for (r=0; r<h; r++) {
1136         memcpy(tile_ptr, fb_ptr, tile_pitch);
1137         fb_ptr += fb_pitch;
1138         tile_ptr += tile_pitch;
1139       }
1140       BX_GUI_THIS graphics_tile_update(tile, xc, yc);
1141     }
1142   }
1143   delete [] tile;
1144 }
1145 
draw_char_common(Bit8u ch,Bit8u fc,Bit8u bc,Bit16u xc,Bit16u yc,Bit8u fw,Bit8u fh,Bit8u fx,Bit8u fy,bool gfxcharw9,Bit8u cs,Bit8u ce,bool curs)1146 void bx_gui_c::draw_char_common(Bit8u ch, Bit8u fc, Bit8u bc, Bit16u xc,
1147                                 Bit16u yc, Bit8u fw, Bit8u fh, Bit8u fx,
1148                                 Bit8u fy, bool gfxcharw9, Bit8u cs, Bit8u ce,
1149                                 bool curs)
1150 {
1151   Bit8u *buf, *font_ptr, fontpixels;
1152   Bit16u font_row, mask;
1153   bool dwidth;
1154 
1155   buf = BX_GUI_THIS snapshot_buffer + yc * BX_GUI_THIS guest_xres + xc;
1156   dwidth = (BX_GUI_THIS guest_fwidth > 9);
1157   font_ptr = &vga_charmap[(ch << 5) + fy];
1158   do {
1159     font_row = *font_ptr++;
1160     if (gfxcharw9) {
1161       font_row = (font_row << 1) | (font_row & 0x01);
1162     } else {
1163       font_row <<= 1;
1164     }
1165     if (fx > 0) {
1166       font_row <<= fx;
1167     }
1168     fontpixels = fw;
1169     if (curs && (fy >= cs) && (fy <= ce))
1170       mask = 0x100;
1171     else
1172       mask = 0x00;
1173     do {
1174       if ((font_row & 0x100) == mask)
1175         *buf = bc;
1176       else
1177         *buf = fc;
1178       buf++;
1179       if (!dwidth || (fontpixels & 1)) font_row <<= 1;
1180     } while (--fontpixels);
1181     buf += (BX_GUI_THIS guest_xres - fw);
1182     fy++;
1183   } while (--fh);
1184 }
1185 
text_update_common(Bit8u * old_text,Bit8u * new_text,Bit16u cursor_address,bx_vga_tminfo_t * tm_info)1186 void bx_gui_c::text_update_common(Bit8u *old_text, Bit8u *new_text,
1187                                   Bit16u cursor_address,
1188                                   bx_vga_tminfo_t *tm_info)
1189 {
1190   Bit16u curs, cursor_x, cursor_y, xc, yc, rows, hchars, text_cols;
1191   Bit16u offset, loffset;
1192   Bit8u cfheight, cfwidth, cfrow, cfcol, fgcolor, bgcolor;
1193   Bit8u split_textrow, split_fontrows, x, y;
1194   Bit8u *new_line, *old_line, *text_base;
1195   bool cursor_visible, gfxcharw9, split_screen;
1196   bool forceUpdate = 0, blink_mode = 0, blink_state = 0;
1197 
1198   if (BX_GUI_THIS snapshot_mode || BX_GUI_THIS new_text_api) {
1199     cursor_visible = ((tm_info->cs_start <= tm_info->cs_end) &&
1200                       (tm_info->cs_start < BX_GUI_THIS guest_fheight));
1201     if (BX_GUI_THIS snapshot_mode && (BX_GUI_THIS snapshot_buffer != NULL)) {
1202       forceUpdate = 1;
1203     } else if (BX_GUI_THIS new_text_api) {
1204       blink_mode = (tm_info->blink_flags & BX_TEXT_BLINK_MODE) > 0;
1205       blink_state = (tm_info->blink_flags & BX_TEXT_BLINK_STATE) > 0;
1206       if (blink_mode) {
1207         if (tm_info->blink_flags & BX_TEXT_BLINK_TOGGLE)
1208           forceUpdate = 1;
1209         if (!blink_state) cursor_visible = 0;
1210       }
1211       if (BX_GUI_THIS charmap_updated) {
1212         BX_GUI_THIS set_font(tm_info->line_graphics);
1213         BX_GUI_THIS charmap_updated = 0;
1214         forceUpdate = 1;
1215       }
1216       if ((tm_info->h_panning != BX_GUI_THIS tm_info.h_panning) ||
1217           (tm_info->v_panning != BX_GUI_THIS tm_info.v_panning) ||
1218           (tm_info->line_compare != BX_GUI_THIS tm_info.line_compare)) {
1219         BX_GUI_THIS tm_info.h_panning = tm_info->h_panning;
1220         BX_GUI_THIS tm_info.v_panning = tm_info->v_panning;
1221         BX_GUI_THIS tm_info.line_compare = tm_info->line_compare;
1222         forceUpdate = 1;
1223       }
1224       // invalidate character at previous and new cursor location
1225       if (cursor_address != BX_GUI_THIS cursor_address) {
1226         old_text[BX_GUI_THIS cursor_address] = ~new_text[BX_GUI_THIS cursor_address];
1227         BX_GUI_THIS cursor_address = cursor_address;
1228       }
1229       if (cursor_address < 0xffff) {
1230         old_text[cursor_address] = ~new_text[cursor_address];
1231       }
1232     }
1233     rows = BX_GUI_THIS guest_yres / BX_GUI_THIS guest_fheight;
1234     if (tm_info->v_panning > 0) rows++;
1235     text_cols = BX_GUI_THIS guest_xres / BX_GUI_THIS guest_fwidth;
1236     if (cursor_visible) {
1237       curs = cursor_address;
1238     } else {
1239       curs = 0xffff;
1240     }
1241     if (tm_info->line_compare < 0x3ff) {
1242       split_textrow = (tm_info->line_compare + tm_info->v_panning) / BX_GUI_THIS guest_fheight;
1243       split_fontrows = ((tm_info->line_compare + tm_info->v_panning) % BX_GUI_THIS guest_fheight) + 1;
1244     } else {
1245       split_textrow = 0xff;
1246       split_fontrows = 0;
1247     }
1248     y = 0;
1249     yc = 0;
1250     split_screen = 0;
1251     loffset = tm_info->start_address;
1252     text_base = new_text;
1253     new_text += tm_info->start_address;
1254     old_text += tm_info->start_address;
1255     do {
1256       hchars = text_cols;
1257       if (tm_info->h_panning > 0) hchars++;
1258       cfheight = BX_GUI_THIS guest_fheight;
1259       cfrow = 0;
1260       if (split_screen) {
1261         if (rows == 1) {
1262           cfheight = (guest_yres - tm_info->line_compare - 1) % BX_GUI_THIS guest_fheight;
1263           if (cfheight == 0) cfheight = BX_GUI_THIS guest_fheight;
1264         }
1265       } else if (tm_info->v_panning > 0) {
1266         if (y == 0) {
1267           cfheight -= tm_info->v_panning;
1268           cfrow = tm_info->v_panning;
1269         } else if (rows == 1) {
1270           cfheight = tm_info->v_panning;
1271         }
1272       }
1273       if (y == split_textrow) {
1274         cfheight = split_fontrows - cfrow;
1275       }
1276       new_line = new_text;
1277       old_line = old_text;
1278       offset = loffset;
1279       x = 0;
1280       xc = 0;
1281       do {
1282         cfwidth = BX_GUI_THIS guest_fwidth;
1283         cfcol = 0;
1284         if (tm_info->h_panning > 0) {
1285           if (x == 0) {
1286             cfcol = tm_info->h_panning;
1287             cfwidth -= tm_info->h_panning;
1288           } else if (hchars == 1) {
1289             cfwidth = tm_info->h_panning;
1290           }
1291         }
1292         // check if char needs to be updated
1293         if (forceUpdate || (new_text[0] != old_text[0]) ||
1294             (new_text[1] != old_text[1])) {
1295           fgcolor = tm_info->actl_palette[new_text[1] & 0x0f];
1296           if (blink_mode) {
1297             bgcolor = tm_info->actl_palette[(new_text[1] >> 4) & 0x07];
1298             if (!blink_state && (new_text[1] & 0x80))
1299               fgcolor = bgcolor;
1300           } else {
1301             bgcolor = tm_info->actl_palette[(new_text[1] >> 4) & 0x0F];
1302           }
1303           gfxcharw9 = ((tm_info->line_graphics) && ((new_text[0] & 0xE0) == 0xC0));
1304           if (BX_GUI_THIS snapshot_mode) {
1305             BX_GUI_THIS draw_char_common(new_text[0], fgcolor, bgcolor, xc, yc,
1306                                          cfwidth, cfheight, cfcol, cfrow,
1307                                          gfxcharw9, tm_info->cs_start,
1308                                          tm_info->cs_end, (offset == curs));
1309           } else {
1310             BX_GUI_THIS draw_char(new_text[0], fgcolor, bgcolor, xc, yc,
1311                                   cfwidth, cfheight, cfcol, cfrow,
1312                                   gfxcharw9, tm_info->cs_start,
1313                                   tm_info->cs_end, (offset == curs));
1314           }
1315         }
1316         new_text += 2;
1317         old_text += 2;
1318         offset += 2;
1319         x++;
1320         xc += cfwidth;
1321       } while (--hchars);
1322       if (y == split_textrow) {
1323         new_text = text_base;
1324         forceUpdate = 1;
1325         loffset = 0;
1326         rows = ((guest_yres - tm_info->line_compare + BX_GUI_THIS guest_fheight - 2) / BX_GUI_THIS guest_fheight) + 1;
1327         if (tm_info->split_hpanning) tm_info->h_panning = 0;
1328         split_screen = 1;
1329       } else {
1330         new_text = new_line + tm_info->line_offset;
1331         old_text = old_line + tm_info->line_offset;
1332         loffset += tm_info->line_offset;
1333       }
1334       y++;
1335       yc += cfheight;
1336     } while (--rows);
1337   } else {
1338     // workarounds for existing text_update() API
1339     if (cursor_address >= tm_info->start_address) {
1340       cursor_x = ((cursor_address - tm_info->start_address) % tm_info->line_offset) / 2;
1341       cursor_y = ((cursor_address - tm_info->start_address) / tm_info->line_offset);
1342     } else {
1343       cursor_x = 0xffff;
1344       cursor_y = 0xffff;
1345     }
1346     if ((tm_info->blink_flags & BX_TEXT_BLINK_STATE) == 0) {
1347       tm_info->cs_start |= 0x20;
1348     }
1349     new_text += tm_info->start_address;
1350     old_text += tm_info->start_address;
1351     text_update(old_text, new_text, cursor_x, cursor_y, tm_info);
1352   }
1353 }
1354 
graphics_tile_update_common(Bit8u * tile,unsigned x,unsigned y)1355 void bx_gui_c::graphics_tile_update_common(Bit8u *tile, unsigned x, unsigned y)
1356 {
1357   unsigned i, pitch, pixel_bytes, nbytes, tilebytes;
1358   Bit8u *src, *dst;
1359 
1360   if (BX_GUI_THIS snapshot_mode) {
1361     if (BX_GUI_THIS snapshot_buffer != NULL) {
1362       pixel_bytes = ((BX_GUI_THIS guest_bpp + 1) >> 3);
1363       tilebytes = BX_GUI_THIS x_tilesize * pixel_bytes;
1364       if ((x + BX_GUI_THIS x_tilesize) <= BX_GUI_THIS guest_xres) {
1365         nbytes = tilebytes;
1366       } else {
1367         nbytes = (BX_GUI_THIS guest_xres - x) * pixel_bytes;
1368       }
1369       pitch = BX_GUI_THIS guest_xres * pixel_bytes;
1370       src = tile;
1371       dst = BX_GUI_THIS snapshot_buffer + (y * pitch) + x;
1372       for (i = 0; i < y_tilesize; i++) {
1373         memcpy(dst, src, nbytes);
1374         src += tilebytes;
1375         dst += pitch;
1376         if (++y >= BX_GUI_THIS guest_yres) break;
1377       }
1378     }
1379   } else {
1380     graphics_tile_update(tile, x, y);
1381   }
1382 }
1383 
graphics_tile_info_common(bx_svga_tileinfo_t * info)1384 bx_svga_tileinfo_t * bx_gui_c::graphics_tile_info_common(bx_svga_tileinfo_t *info)
1385 {
1386   if (!info) {
1387     info = new bx_svga_tileinfo_t;
1388     if (!info) {
1389       return NULL;
1390     }
1391   }
1392   info->snapshot_mode = BX_GUI_THIS snapshot_mode;
1393   if (BX_GUI_THIS snapshot_mode) {
1394     info->pitch = BX_GUI_THIS guest_xres * ((BX_GUI_THIS guest_bpp + 1) >> 3);
1395   } else {
1396     return graphics_tile_info(info);
1397   }
1398 
1399   return info;
1400 }
1401 
1402 #if BX_USE_GUI_CONSOLE
1403 
1404 #define BX_CONSOLE_BUFSIZE 4000
1405 
console_init(void)1406 void bx_gui_c::console_init(void)
1407 {
1408   int i;
1409 
1410   console.screen = new Bit8u[BX_CONSOLE_BUFSIZE];
1411   console.oldscreen = new Bit8u[BX_CONSOLE_BUFSIZE];
1412   for (i = 0; i < BX_CONSOLE_BUFSIZE; i+=2) {
1413     console.screen[i] = 0x20;
1414     console.screen[i+1] = 0x07;
1415   }
1416   memset(console.oldscreen, 0xff, BX_CONSOLE_BUFSIZE);
1417   console.saved_xres = guest_xres;
1418   console.saved_yres = guest_yres;
1419   console.saved_bpp = guest_bpp;
1420   console.saved_fwidth = guest_fwidth;
1421   console.saved_fheight = guest_fheight;
1422   memcpy(console.saved_charmap, BX_GUI_THIS vga_charmap, 0x2000);
1423   for (i = 0; i < 256; i++) {
1424     memcpy(&BX_GUI_THIS vga_charmap[0]+i*32, &sdl_font8x16[i], 16);
1425     BX_GUI_THIS char_changed[i] = 1;
1426   }
1427   BX_GUI_THIS charmap_updated = 1;
1428   console.cursor_x = 0;
1429   console.cursor_y = 0;
1430   console.cursor_addr = 0;
1431   memset(&console.tminfo, 0, sizeof(bx_vga_tminfo_t));
1432   console.tminfo.line_offset = 160;
1433   console.tminfo.line_compare = 1023;
1434   console.tminfo.cs_start = 0x2e;
1435   console.tminfo.cs_end = 0x0f;
1436   console.tminfo.actl_palette[7] = 0x07;
1437   memcpy(console.saved_palette, palette, 32);
1438   palette_change_common(0x00, 0x00, 0x00, 0x00);
1439   palette_change_common(0x07, 0xa8, 0xa8, 0xa8);
1440   dimension_update(720, 400, 16, 9, 8);
1441   console.n_keys = 0;
1442   console.running = 1;
1443 }
1444 
console_cleanup(void)1445 void bx_gui_c::console_cleanup(void)
1446 {
1447   delete [] console.screen;
1448   delete [] console.oldscreen;
1449   palette_change_common(0x00, console.saved_palette[2], console.saved_palette[1],
1450                         console.saved_palette[0]);
1451   palette_change_common(0x07, console.saved_palette[30], console.saved_palette[29],
1452                         console.saved_palette[28]);
1453   unsigned fheight = (console.saved_fheight);
1454   unsigned fwidth = (console.saved_fwidth);
1455   set_text_charmap(console.saved_charmap);
1456   dimension_update(console.saved_xres, console.saved_yres, fheight, fwidth,
1457                    console.saved_bpp);
1458   DEV_vga_refresh(1);
1459   console.running = 0;
1460 }
1461 
console_refresh(bool force)1462 void bx_gui_c::console_refresh(bool force)
1463 {
1464   if (force) memset(console.oldscreen, 0xff, BX_CONSOLE_BUFSIZE);
1465   if (BX_GUI_THIS new_text_api) {
1466     text_update_common(console.oldscreen, console.screen, console.cursor_addr,
1467                        &console.tminfo);
1468   } else {
1469     text_update(console.oldscreen, console.screen, console.cursor_x,
1470                 console.cursor_y, &console.tminfo);
1471   }
1472   flush();
1473   memcpy(console.oldscreen, console.screen, BX_CONSOLE_BUFSIZE);
1474 }
1475 
console_key_enq(Bit8u key)1476 void bx_gui_c::console_key_enq(Bit8u key)
1477 {
1478   if (console.n_keys < 16) {
1479     console.keys[console.n_keys++] = key;
1480   }
1481 }
1482 
bx_printf(const char * s)1483 int bx_gui_c::bx_printf(const char *s)
1484 {
1485   unsigned offset;
1486 
1487   if (!console.running) {
1488     console_init();
1489   }
1490   for (unsigned i = 0; i < strlen(s); i++) {
1491     offset = console.cursor_y * 160 + console.cursor_x * 2;
1492     if ((s[i] != 0x08) && (s[i] != 0x0a)) {
1493       console.screen[offset] = s[i];
1494       console.screen[offset+1] = 0x07;
1495       console.cursor_x++;
1496     }
1497     if ((s[i] == 0x0a) || (console.cursor_x == 80)) {
1498       console.cursor_x = 0;
1499       console.cursor_y++;
1500     }
1501     if (s[i] == 0x08) {
1502       if (offset > 0) {
1503         console.screen[offset-2] = 0x20;
1504         console.screen[offset-1] = 0x07;
1505         if (console.cursor_x > 0) {
1506           console.cursor_x--;
1507         } else {
1508           console.cursor_x = 79;
1509           console.cursor_y--;
1510         }
1511       }
1512     }
1513     if (console.cursor_y == 25) {
1514       memmove(console.screen, console.screen+160, BX_CONSOLE_BUFSIZE-160);
1515       console.cursor_y--;
1516       offset = console.cursor_y * 160 + console.cursor_x * 2;
1517       for (int j = 0; j < 160; j+=2) {
1518         console.screen[offset+j] = 0x20;
1519         console.screen[offset+j+1] = 0x07;
1520       }
1521     }
1522   }
1523   console.cursor_addr = console.cursor_y * 160 + console.cursor_x * 2;
1524   console_refresh(0);
1525   return strlen(s);
1526 }
1527 
bx_gets(char * s,int size)1528 char* bx_gui_c::bx_gets(char *s, int size)
1529 {
1530   char keystr[2];
1531   int pos = 0, done = 0;
1532   int cs_counter = 1, cs_visible = 0;
1533 
1534   set_console_edit_mode(1);
1535   keystr[1] = 0;
1536   do {
1537     handle_events();
1538     while (console.n_keys > 0) {
1539       if ((console.keys[0] >= 0x20) && (pos < (size-1))) {
1540         s[pos++] = console.keys[0];
1541         keystr[0] = console.keys[0];
1542         bx_printf(keystr);
1543       } else if (console.keys[0] == 0x0d) {
1544         s[pos] = 0x00;
1545         keystr[0] = 0x0a;
1546         bx_printf(keystr);
1547         done = 1;
1548       } else if ((console.keys[0] == 0x08) && (pos > 0)) {
1549         pos--;
1550         keystr[0] = 0x08;
1551         bx_printf(keystr);
1552       }
1553       memmove(&console.keys[0], &console.keys[1], 15);
1554       console.n_keys--;
1555     }
1556 #if BX_HAVE_USLEEP
1557     usleep(25000);
1558 #else
1559     msleep(25);
1560 #endif
1561     if (--cs_counter == 0) {
1562       cs_counter = 10;
1563       cs_visible ^= 1;
1564       if (cs_visible) {
1565         console.tminfo.cs_start &= ~0x20;
1566       } else {
1567         console.tminfo.cs_start |= 0x20;
1568       }
1569       console_refresh(0);
1570     }
1571   } while (!done);
1572   console.tminfo.cs_start |= 0x20;
1573   set_console_edit_mode(0);
1574   return s;
1575 }
1576 #endif
1577 
set_command_mode(bool active)1578 void bx_gui_c::set_command_mode(bool active)
1579 {
1580   if (command_mode.present) {
1581     command_mode.active = active;
1582   }
1583 }
1584