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