]> Shamusworld >> Repos - apple2/blob - src/gui/gui.cpp
Removed GUI cruft, added 10 & 12pt fonts.
[apple2] / src / gui / gui.cpp
1 //
2 // gui.cpp
3 //
4 // Graphical User Interface support
5 // by James Hammons
6 // © 2014 Underground Software
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // WHO  WHEN        WHAT
11 // ---  ----------  ------------------------------------------------------------
12 // JLH  02/03/2006  Created this file
13 // JLH  03/13/2006  Added functions to allow shutting down GUI externally
14 // JLH  03/22/2006  Finalized basic multiple window support
15 // JLH  03/03/2014  Refactored GUI to use SDL 2, more modern approach as well
16 //
17 // STILL TO DO:
18 //
19 // - Memory leak on quitting with a window active [DONE]
20 // - Multiple window handling [DONE]
21 //
22
23 #include "gui.h"
24 #include "apple2.h"
25 #include "applevideo.h"
26 #include "diskselector.h"
27 #include "log.h"
28 #include "video.h"
29
30 // Icons, in GIMP "C" format
31 #include "gfx/icon-selection.c"
32 #include "gfx/disk-icon.c"
33 #include "gfx/power-off-icon.c"
34 #include "gfx/power-on-icon.c"
35 #include "gfx/disk-swap-icon.c"
36 #include "gfx/disk-door-open.c"
37 #include "gfx/disk-door-closed.c"
38 #include "gfx/save-state-icon.c"
39 #include "gfx/load-state-icon.c"
40 #include "gfx/config-icon.c"
41
42
43 // Okay, this is ugly but works and I can't think of any better way to handle
44 // this. So what we do when we pass the GIMP bitmaps into a function is pass
45 // them as a (void *) and then cast them as type (Bitmap *) in order to use
46 // them. Yes, it's ugly. Come up with something better!
47
48 struct Bitmap {
49         unsigned int width;
50         unsigned int height;
51         unsigned int bytesPerPixel;                                     // 3:RGB, 4:RGBA
52         unsigned char pixelData[];
53 };
54
55
56 const char numeralOne[(7 * 7) + 1] =
57         "  @@   "
58         " @@@   "
59         "@@@@   "
60         "  @@   "
61         "  @@   "
62         "  @@   "
63         "@@@@@@ ";
64
65 const char numeralTwo[(7 * 7) + 1] =
66         " @@@@@ "
67         "@@   @@"
68         "    @@@"
69         "  @@@@ "
70         " @@@   "
71         "@@     "
72         "@@@@@@@";
73
74 const char ejectIcon[(8 * 7) + 1] =
75         "   @@   "
76         "  @@@@  "
77         " @@@@@@ "
78         "@@@@@@@@"
79         "        "
80         "@@@@@@@@"
81         "@@@@@@@@";
82
83 const char driveLight[(5 * 5) + 1] =
84         " @@@ "
85         "@@@@@"
86         "@@@@@"
87         "@@@@@"
88         " @@@ ";
89
90
91 enum { SBS_SHOWING, SBS_HIDING, SBS_SHOWN, SBS_HIDDEN };
92
93
94 SDL_Texture * GUI::overlay = NULL;
95 SDL_Rect GUI::olDst;
96 int GUI::sidebarState = SBS_HIDDEN;
97 int32_t GUI::dx = 0;
98 int32_t GUI::iconSelected = -1;
99 bool GUI::hasKeyboardFocus = false;
100 int32_t lastIconSelected = -1;
101 SDL_Texture * iconSelection = NULL;
102 SDL_Texture * diskIcon = NULL;
103 SDL_Texture * disk1Icon = NULL;
104 SDL_Texture * disk2Icon = NULL;
105 SDL_Texture * powerOnIcon = NULL;
106 SDL_Texture * powerOffIcon = NULL;
107 SDL_Texture * diskSwapIcon = NULL;
108 SDL_Texture * stateSaveIcon = NULL;
109 SDL_Texture * stateLoadIcon = NULL;
110 SDL_Texture * configIcon = NULL;
111 SDL_Texture * doorOpen = NULL;
112 SDL_Texture * doorClosed = NULL;
113 uint32_t texturePointer[128 * 380];
114 const char iconHelp[7][80] = { "Turn emulated Apple off/on",
115         "Insert floppy image into drive #1", "Insert floppy image into drive #2",
116         "Swap disks", "Save emulator state", "Load emulator state",
117         "Configure Apple2" };
118
119 #define SIDEBAR_X_POS  (VIRTUAL_SCREEN_WIDTH - 80)
120
121
122 GUI::GUI(void)
123 {
124 }
125
126
127 GUI::~GUI(void)
128 {
129 }
130
131
132 void GUI::Init(SDL_Renderer * renderer)
133 {
134         overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
135                 SDL_TEXTUREACCESS_TARGET, 128, 380);
136
137         if (!overlay)
138         {
139                 WriteLog("GUI: Could not create overlay!\n");
140                 return;
141         }
142
143         if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
144                 WriteLog("GUI: Could not set blend mode for overlay.\n");
145
146         for(uint32_t i=0; i<128*380; i++)
147                 texturePointer[i] = 0xB0A000A0;
148
149         SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
150
151         olDst.x = VIRTUAL_SCREEN_WIDTH;
152         olDst.y = 2;
153         olDst.w = 128;
154         olDst.h = 380;
155
156         iconSelection  = CreateTexture(renderer, &icon_selection);
157         diskIcon       = CreateTexture(renderer, &disk_icon);
158         doorOpen       = CreateTexture(renderer, &door_open);
159         doorClosed     = CreateTexture(renderer, &door_closed);
160         disk1Icon      = CreateTexture(renderer, &disk_icon);
161         disk2Icon      = CreateTexture(renderer, &disk_icon);
162         powerOffIcon   = CreateTexture(renderer, &power_off);
163         powerOnIcon    = CreateTexture(renderer, &power_on);
164         diskSwapIcon   = CreateTexture(renderer, &disk_swap);
165         stateSaveIcon  = CreateTexture(renderer, &save_state);
166         stateLoadIcon  = CreateTexture(renderer, &load_state);
167         configIcon     = CreateTexture(renderer, &config);
168
169         // Set up drive icons in their current states
170 //      AssembleDriveIcon(renderer, 0);
171 //      AssembleDriveIcon(renderer, 1);
172
173         if (SDL_SetRenderTarget(renderer, overlay) < 0)
174         {
175                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
176         }
177         else
178         {
179                 DrawSidebarIcons(renderer);
180                 // Set render target back to default
181                 SDL_SetRenderTarget(renderer, NULL);
182         }
183
184         DiskSelector::Init(renderer);
185         DiskSelector::showWindow = true;
186
187         WriteLog("GUI: Successfully initialized.\n");
188 }
189
190
191 SDL_Texture * GUI::CreateTexture(SDL_Renderer * renderer, const void * source)
192 {
193         Bitmap * bitmap = (Bitmap *)source;
194         SDL_Texture * texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
195 //              SDL_TEXTUREACCESS_STATIC, bitmap->width, bitmap->height);
196                 SDL_TEXTUREACCESS_TARGET, bitmap->width, bitmap->height);
197         SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
198         SDL_UpdateTexture(texture, NULL, (Uint32 *)bitmap->pixelData,
199                 bitmap->width * sizeof(Uint32));
200
201         return texture;
202 }
203
204
205 void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
206 {
207 }
208
209
210 void GUI::MouseUp(int32_t x, int32_t y, uint32_t buttons)
211 {
212 }
213
214
215 void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
216 {
217         if (sidebarState != SBS_SHOWN)
218         {
219                 iconSelected = -1;
220
221                 if (x > SIDEBAR_X_POS)
222                 {
223 //printf("GUI: sidebar showing (x = %i)...\n", x);
224                         sidebarState = SBS_SHOWING;
225                         dx = -8;
226                 }
227                 else
228                 {
229 //printf("GUI: sidebar hiding[1] (x = %i)...\n", x);
230                         sidebarState = SBS_HIDING;
231                         dx = 8;
232                 }
233         }
234         else
235         {
236                 if (x < SIDEBAR_X_POS)
237                 {
238                         iconSelected = lastIconSelected = -1;
239                         HandleIconSelection(sdlRenderer);
240 //printf("GUI: sidebar hiding[2] (x = %i)...\n", x);
241                         sidebarState = SBS_HIDING;
242                         dx = 8;
243                 }
244                 // We're in the right zone, and the sidebar is shown, so let's select
245                 // something!
246                 else
247                 {
248                         if (y < 4 || y > 383)
249                         {
250                                 iconSelected = -1;
251                         }
252                         else
253                                 iconSelected = (y - 4) / 54;
254
255                         if (iconSelected != lastIconSelected)
256                         {
257                                 HandleIconSelection(sdlRenderer);
258                                 lastIconSelected = iconSelected;
259                                 SpawnMessage("%s", iconHelp[iconSelected]);
260
261                                 // Show what's in the selected drive
262                                 if (iconSelected >= 1 && iconSelected <= 2)
263                                 {
264                                         if (!floppyDrive.IsEmpty(iconSelected - 1))
265                                                 SpawnMessage("\"%s\"", floppyDrive.ImageName(iconSelected - 1));
266                                 }
267                         }
268                 }
269         }
270 }
271
272
273 void GUI::HandleIconSelection(SDL_Renderer * renderer)
274 {
275         // Set up drive icons in their current states
276         AssembleDriveIcon(renderer, 0);
277         AssembleDriveIcon(renderer, 1);
278
279         // Reload the background...
280         SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
281
282         if (SDL_SetRenderTarget(renderer, overlay) < 0)
283         {
284                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
285                 return;
286         }
287
288         // Draw the icon selector, if an icon is selected
289         if (iconSelected >= 0)
290         {
291                 SDL_Rect dst;// = { 54, 54, 24 - 7, 2 };
292                 dst.w = dst.h = 54, dst.x = 24 - 7, dst.y = 2 + (iconSelected * 54);
293                 SDL_RenderCopy(renderer, iconSelection, NULL, &dst);
294         }
295
296         DrawSidebarIcons(renderer);
297
298         // Set render target back to default
299         SDL_SetRenderTarget(renderer, NULL);
300 }
301
302
303 void GUI::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
304 {
305         SDL_Texture * drive[2] = { disk1Icon, disk2Icon };
306         const char * number[2] = { numeralOne, numeralTwo };
307
308         if (SDL_SetRenderTarget(renderer, drive[driveNumber]) < 0)
309         {
310                 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
311                 return;
312         }
313
314         SDL_RenderClear(renderer);
315         SDL_RenderCopy(renderer, diskIcon, NULL, NULL);
316
317         // Drive door @ (16, 7)
318         SDL_Rect dst;
319         dst.w = 8, dst.h = 10, dst.x = 16, dst.y = 7;
320         SDL_RenderCopy(renderer, (floppyDrive.IsEmpty(driveNumber) ?
321                 doorOpen : doorClosed), NULL, &dst);
322
323         // Numeral @ (30, 20)
324         DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
325         DrawDriveLight(renderer, driveNumber);
326         DrawEjectButton(renderer, driveNumber);
327
328         // Set render target back to default
329         SDL_SetRenderTarget(renderer, NULL);
330 }
331
332
333 void GUI::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
334 {
335         if (floppyDrive.IsEmpty(driveNumber))
336                 return;
337
338         DrawCharArray(renderer, ejectIcon, 29, 31, 8, 7, 0x00, 0xAA, 0x00);
339 }
340
341
342 void GUI::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
343 {
344         int lightState = floppyDrive.DriveLightStatus(driveNumber);
345         int r = 0x77, g = 0x00, b = 0x00;
346
347         if (lightState == DLS_READ)
348                 r = 0x20, g = 0xFF, b = 0x20;
349         else if (lightState == DLS_WRITE)
350                 r = 0xFF, g = 0x30, b = 0x30;
351
352         // Drive light @ (8, 21)
353         DrawCharArray(renderer, driveLight, 8, 21, 5, 5, r, g, b);
354 }
355
356
357 void GUI::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
358         int y, int w, int h, int r, int g, int b)
359 {
360         SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
361
362         for(int j=0; j<h; j++)
363         {
364                 for(int i=0; i<w; i++)
365                 {
366                         if (array[(j * w) + i] != ' ')
367                                 SDL_RenderDrawPoint(renderer, x + i, y + j);
368                 }
369         }
370
371         SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
372 }
373
374
375 void GUI::HandleGUIState(void)
376 {
377         olDst.x += dx;
378
379         if (olDst.x < SIDEBAR_X_POS && sidebarState == SBS_SHOWING)
380         {
381                 olDst.x = SIDEBAR_X_POS;
382                 sidebarState = SBS_SHOWN;
383                 dx = 0;
384         }
385         else if (olDst.x > VIRTUAL_SCREEN_WIDTH && sidebarState == SBS_HIDING)
386         {
387                 olDst.x = VIRTUAL_SCREEN_WIDTH;
388                 sidebarState = SBS_HIDDEN;
389                 dx = 0;
390         }
391 }
392
393
394 void GUI::DrawSidebarIcons(SDL_Renderer * renderer)
395 {
396         SDL_Texture * icons[7] = { powerOnIcon, disk1Icon, disk2Icon, diskSwapIcon,
397                 stateSaveIcon, stateLoadIcon, configIcon };
398
399         SDL_Rect dst;
400         dst.w = dst.h = 40, dst.x = 24, dst.y = 2 + 7;
401
402         for(int i=0; i<7; i++)
403         {
404                 SDL_RenderCopy(renderer, icons[i], NULL, &dst);
405                 dst.y += 54;
406         }
407 }
408
409
410 void GUI::Render(SDL_Renderer * renderer)
411 {
412         if (!overlay)
413                 return;
414
415         HandleGUIState();
416
417         if (sidebarState != SBS_HIDDEN)
418                 HandleIconSelection(renderer);
419
420         SDL_RenderCopy(renderer, overlay, NULL, &olDst);
421
422         // Hmm.
423         DiskSelector::Render(renderer);
424 }
425
426
427 /*
428 GUI Considerations:
429
430 screen is 560 x 384
431
432 cut into 7 pieces give ~54 pix per piece
433 So, let's try 40x40 icons, and see if that's good enough...
434 Selection is 54x54.
435
436 drive proportions: 1.62 : 1
437
438 Icon order:
439
440 +-----+
441 |     |
442 |Power|
443 |     |
444 +-----+
445
446 +-----+
447 |     |
448 |Disk1|
449 |    ^| <-- eject button
450 +-----+
451
452 +-----+
453 |     |
454 |Disk2|
455 |    ^|
456 +-----+
457
458 +-----+
459 |     |
460 |Swap |
461 |     |
462 +-----+
463
464 +-----+
465 |     |
466 |Confg|
467 |     |
468 +-----+
469
470 maybe state save/load
471
472 */
473