|
|
1.1 root 1: /*
2: Hatari - statusbar.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: Code to draw statusbar area, floppy leds etc.
1.1.1.3 ! root 8:
1.1 root 9: Use like this:
10: - Before screen surface is (re-)created Statusbar_SetHeight()
11: has to be called with the new screen height. Add the returned
12: value to screen height (zero means no statusbar). After this,
13: Statusbar_GetHeight() can be used to retrieve the statusbar size
14: - After screen surface is (re-)created, call Statusbar_Init()
15: to re-initialize / re-draw the statusbar
16: - Call Statusbar_SetFloppyLed() to set floppy drive led ON/OFF,
17: or call Statusbar_EnableHDLed() to enabled HD led for a while
18: - Whenever screen is redrawn, call Statusbar_Update() to draw the
19: updated information to the statusbar (outside of screen locking)
20: - If screen redraws may be partial, Statusbar_OverlayRestore()
21: needs to be called before locking the screen for drawing and
22: Statusbar_OverlayBackup() needs to be called after screen unlocking,
23: but before calling Statusbar_Update(). These are needed for
24: hiding the overlay drive led when drive leds are turned OFF.
25: - If other information shown by Statusbar (TOS version etc) changes,
26: call Statusbar_UpdateInfo()
27:
28: TODO:
29: - re-calculate colors on each update to make sure they're
30: correct in Falcon & TT 8-bit palette modes?
31: - call Statusbar_AddMessage() from log.c?
32: */
1.1.1.3 ! root 33: const char Statusbar_fileid[] = "Hatari statusbar.c : " __DATE__ " " __TIME__;
1.1 root 34:
35: #include <assert.h>
36: #include "main.h"
37: #include "configuration.h"
38: #include "screenSnapShot.h"
39: #include "sdlgui.h"
40: #include "statusbar.h"
41: #include "tos.h"
42: #include "video.h"
43: #include "wavFormat.h"
44: #include "ymFormat.h"
45:
46: #define DEBUG 0
47: #if DEBUG
48: #define DEBUGPRINT(x) printf x
49: #else
50: #define DEBUGPRINT(x)
51: #endif
52:
53: #define MAX_DRIVE_LEDS (DRIVE_LED_HD + 1)
54:
55: /* whether drive leds should be ON and their previous shown state */
56: static struct {
57: bool state;
58: bool oldstate;
59: Uint32 expire; /* when to disable led, valid only if >0 && state=TRUE */
60: int offset; /* led x-pos on screen */
61: } Led[MAX_DRIVE_LEDS];
62:
63: /* drive leds size & y-pos */
64: static SDL_Rect LedRect;
65:
66: /* overlay led size & pos */
67: static SDL_Rect OverlayLedRect;
68:
69: /* screen contents left under overlay led */
70: static SDL_Surface *OverlayUnderside;
71:
72: static enum {
73: OVERLAY_NONE,
74: OVERLAY_DRAWN,
75: OVERLAY_RESTORED
76: } bOverlayState;
77:
78: static SDL_Rect RecLedRect;
79: static bool bOldRecording;
80:
81: /* led colors */
82: static Uint32 LedColorOn, LedColorOff, RecColorOn, RecColorOff;
83: static Uint32 GrayBg, LedColorBg;
84:
85:
86: #define MAX_MESSAGE_LEN 23
87: typedef struct msg_item {
88: struct msg_item *next;
89: char msg[MAX_MESSAGE_LEN+1];
90: Uint32 timeout; /* msecs, zero=no timeout */
91: Uint32 expire; /* when to expire message */
92: bool shown;
93: } msg_item_t;
94:
95: static msg_item_t DefaultMessage;
96: static msg_item_t *MessageList = &DefaultMessage;
97: static SDL_Rect MessageRect;
98:
99: static SDL_Rect FrameSkipsRect;
100: static int nOldFrameSkips;
101:
102:
103: /* screen height above statusbar and height of statusbar below screen */
104: static int ScreenHeight;
105: static int StatusbarHeight;
106:
107:
108: /*-----------------------------------------------------------------------*/
109: /**
110: * Set screen height used for statusbar height calculation.
111: *
112: * Return height of statusbar that should be added to the screen
113: * height when screen is (re-)created, or zero if statusbar will
114: * not be shown
115: */
116: int Statusbar_SetHeight(int width, int height)
117: {
118: ScreenHeight = height;
119: if (ConfigureParams.Screen.bShowStatusbar) {
120: /* Should check the same thing as SDLGui_SetScreen()
121: * does to decide the font size.
122: */
1.1.1.3 ! root 123: if (width >= 640 && height >= (400-24)) {
1.1 root 124: StatusbarHeight = 24;
125: } else {
126: StatusbarHeight = 12;
127: }
128: } else {
129: StatusbarHeight = 0;
130: }
131: return StatusbarHeight;
132: }
133:
134: /*-----------------------------------------------------------------------*/
135: /**
136: * Return height of statusbar set with Statusbar_SetHeight()
137: */
138: int Statusbar_GetHeight(void)
139: {
140: return StatusbarHeight;
141: }
142:
143:
144: /*-----------------------------------------------------------------------*/
145: /**
146: * Enable HD drive led, it will be automatically disabled after a while.
147: */
148: void Statusbar_EnableHDLed(void)
149: {
150: /* leds are shown for 1/2 sec after enabling */
151: Led[DRIVE_LED_HD].expire = SDL_GetTicks() + 1000/2;
1.1.1.3 ! root 152: Led[DRIVE_LED_HD].state = true;
1.1 root 153: }
154:
155: /*-----------------------------------------------------------------------*/
156: /**
157: * Set given floppy drive led state, anything enabling led with this
158: * needs also to take care of disabling it.
159: */
160: void Statusbar_SetFloppyLed(drive_index_t drive, bool state)
161: {
162: assert(drive == DRIVE_LED_A || drive == DRIVE_LED_B);
163: Led[drive].state = state;
164: }
165:
166:
167: /*-----------------------------------------------------------------------*/
168: /**
169: * Set overlay led size/pos on given screen to internal Rect
170: * and free previous resources.
171: */
172: static void Statusbar_OverlayInit(const SDL_Surface *surf)
173: {
174: int h;
175: /* led size/pos needs to be re-calculated in case screen changed */
176: h = surf->h / 50;
177: OverlayLedRect.w = 2*h;
178: OverlayLedRect.h = h;
179: OverlayLedRect.x = surf->w - 5*h/2;
180: OverlayLedRect.y = h/2;
181: /* free previous restore surface if it's incompatible */
182: if (OverlayUnderside &&
183: OverlayUnderside->w == OverlayLedRect.w &&
184: OverlayUnderside->h == OverlayLedRect.h &&
185: OverlayUnderside->format->BitsPerPixel == surf->format->BitsPerPixel) {
186: SDL_FreeSurface(OverlayUnderside);
187: OverlayUnderside = NULL;
188: }
189: bOverlayState = OVERLAY_NONE;
190: }
191:
192: /*-----------------------------------------------------------------------*/
193: /**
194: * (re-)initialize statusbar internal variables for given screen surface
195: * (sizes&colors may need to be re-calculated for the new SDL surface)
196: * and draw the statusbar background.
197: */
198: void Statusbar_Init(SDL_Surface *surf)
199: {
200: msg_item_t *item;
201: SDL_Rect ledbox, sbarbox;
202: int i, fontw, fonth, offset;
203: const char *text[MAX_DRIVE_LEDS] = { "A:", "B:", "HD:" };
204:
205: assert(surf);
206:
207: /* dark green and light green for leds themselves */
208: LedColorOff = SDL_MapRGB(surf->format, 0x00, 0x40, 0x00);
209: LedColorOn = SDL_MapRGB(surf->format, 0x00, 0xe0, 0x00);
210: LedColorBg = SDL_MapRGB(surf->format, 0x00, 0x00, 0x00);
211: RecColorOff = SDL_MapRGB(surf->format, 0x40, 0x00, 0x00);
212: RecColorOn = SDL_MapRGB(surf->format, 0xe0, 0x00, 0x00);
213: GrayBg = SDL_MapRGB(surf->format, 0xc0, 0xc0, 0xc0);
214:
215: /* disable leds */
216: for (i = 0; i < MAX_DRIVE_LEDS; i++) {
1.1.1.3 ! root 217: Led[i].state = Led[i].oldstate = false;
1.1 root 218: Led[i].expire = 0;
219: }
220: Statusbar_OverlayInit(surf);
221:
222: /* disable statusbar if it doesn't fit to video mode */
223: if (surf->h < ScreenHeight + StatusbarHeight) {
224: StatusbarHeight = 0;
225: }
226: if (!StatusbarHeight) {
227: return;
228: }
229:
230: /* prepare fonts */
231: SDLGui_Init();
232: SDLGui_SetScreen(surf);
233: SDLGui_GetFontSize(&fontw, &fonth);
234:
235: /* video mode didn't match, need to recalculate sizes */
236: if (surf->h > ScreenHeight + StatusbarHeight) {
237: StatusbarHeight = fonth + 2;
238: /* actually statusbar vertical offset */
239: ScreenHeight = surf->h - StatusbarHeight;
240: } else {
241: assert(fonth+2 < StatusbarHeight);
242: }
243:
244: /* draw statusbar background gray so that text shows */
245: sbarbox.x = 0;
246: sbarbox.y = surf->h - StatusbarHeight;
247: sbarbox.w = surf->w;
248: sbarbox.h = StatusbarHeight;
249: SDL_FillRect(surf, &sbarbox, GrayBg);
250:
251: /* led size */
252: LedRect.w = 2*fonth;
253: LedRect.h = fonth - 4;
254: LedRect.y = ScreenHeight + StatusbarHeight/2 - LedRect.h/2;
255:
256: /* black box for the leds */
257: ledbox = LedRect;
258: ledbox.y -= 1;
259: ledbox.w += 2;
260: ledbox.h += 2;
261:
262: offset = fontw;
263: MessageRect.y = LedRect.y - 2;
264: /* draw led texts and boxes + calculate box offsets */
265: for (i = 0; i < MAX_DRIVE_LEDS; i++) {
266: SDLGui_Text(offset, MessageRect.y, text[i]);
267: offset += strlen(text[i]) * fontw;
268: offset += fontw/2;
269:
270: ledbox.x = offset - 1;
271: SDL_FillRect(surf, &ledbox, LedColorBg);
272:
273: LedRect.x = offset;
274: SDL_FillRect(surf, &LedRect, LedColorOff);
275:
276: Led[i].offset = offset;
277: offset += LedRect.w + 2*fontw;
278: }
279:
280: /* draw frameskip */
281: FrameSkipsRect.x = offset;
282: FrameSkipsRect.y = MessageRect.y;
283: SDLGui_Text(FrameSkipsRect.x, FrameSkipsRect.y, "FS:");
284: FrameSkipsRect.x += 3 * fontw + fontw/2;
285: FrameSkipsRect.w = fontw;
286: FrameSkipsRect.h = fonth;
287: SDLGui_Text(FrameSkipsRect.x, FrameSkipsRect.y, "0");
288: nOldFrameSkips = 0;
289:
290: /* intialize messages */
291: MessageRect.x = FrameSkipsRect.x + 2 * fontw;
292: MessageRect.w = MAX_MESSAGE_LEN * fontw;
293: MessageRect.h = fonth;
294: for (item = MessageList; item; item = item->next) {
1.1.1.3 ! root 295: item->shown = false;
1.1 root 296: }
297:
298: /* draw recording led box */
299: RecLedRect = LedRect;
300: RecLedRect.x = surf->w - fontw - RecLedRect.w;
301: ledbox.x = RecLedRect.x - 1;
302: SDLGui_Text(ledbox.x - 4*fontw - fontw/2, MessageRect.y, "REC:");
303: SDL_FillRect(surf, &ledbox, LedColorBg);
304: SDL_FillRect(surf, &RecLedRect, RecColorOff);
1.1.1.3 ! root 305: bOldRecording = false;
1.1 root 306:
307: /* and blit statusbar on screen */
308: SDL_UpdateRects(surf, 1, &sbarbox);
309: DEBUGPRINT(("Draw statusbar\n"));
310: }
311:
312:
313: /*-----------------------------------------------------------------------*/
314: /**
315: * Qeueue new statusbar message 'msg' to be shown for 'msecs' milliseconds
316: */
317: void Statusbar_AddMessage(const char *msg, Uint32 msecs)
318: {
319: msg_item_t *item;
320:
321: if (!ConfigureParams.Screen.bShowStatusbar) {
322: /* no sense in queuing messages that aren't shown */
323: return;
324: }
325: item = calloc(1, sizeof(msg_item_t));
326: assert(item);
327:
328: item->next = MessageList;
329: MessageList = item;
330:
331: strncpy(item->msg, msg, MAX_MESSAGE_LEN);
332: item->msg[MAX_MESSAGE_LEN] = '\0';
333: DEBUGPRINT(("Add message: '%s'\n", item->msg));
334:
335: if (msecs) {
336: item->timeout = msecs;
337: } else {
338: /* show items by default for 2.5 secs */
339: item->timeout = 2500;
340: }
1.1.1.3 ! root 341: item->shown = false;
1.1 root 342: }
343:
344: /*-----------------------------------------------------------------------*/
345: /**
346: * Write given 'more' string to 'buffer' and return new end of 'buffer'
347: */
348: static char *Statusbar_AddString(char *buffer, const char *more)
349: {
350: while(*more) {
351: *buffer++ = *more++;
352: }
353: return buffer;
354: }
355:
356: /*-----------------------------------------------------------------------*/
357: /**
358: * Retrieve/update default statusbar information
359: */
360: void Statusbar_UpdateInfo(void)
361: {
362: char *end = DefaultMessage.msg;
363:
364: /* amount of memory */
365: if (ConfigureParams.Memory.nMemorySize > 9) {
366: *end++ = '1';
367: *end++ = '0' + ConfigureParams.Memory.nMemorySize % 10;
368: } else {
369: if (ConfigureParams.Memory.nMemorySize) {
370: *end++ = '0' + ConfigureParams.Memory.nMemorySize;
371: } else {
372: end = Statusbar_AddString(end, "1/2");
373: }
374: }
375: end = Statusbar_AddString(end, "MB ");
376:
377: /* machine type */
378: switch (ConfigureParams.System.nMachineType) {
379: case MACHINE_ST:
380: end = Statusbar_AddString(end, "ST");
381: break;
382: case MACHINE_STE:
383: end = Statusbar_AddString(end, "STE");
384: break;
385: case MACHINE_TT:
386: end = Statusbar_AddString(end, "TT");
387: break;
388: case MACHINE_FALCON:
389: end = Statusbar_AddString(end, "Falcon");
390: break;
391: default:
392: end = Statusbar_AddString(end, "???");
393: break;
394: }
395:
396: /* TOS type/version */
397: if (bIsEmuTOS) {
398: end = Statusbar_AddString(end, ", EmuTOS");
399: } else {
400: end = Statusbar_AddString(end, ", TOS v");
401: *end++ = '0' + ((TosVersion & 0xf00) >> 8);
402: *end++ = '.';
403: *end++ = '0' + ((TosVersion & 0xf0) >> 4);
404: *end++ = '0' + (TosVersion & 0xf);
405: }
406: *end = '\0';
407: assert(end - DefaultMessage.msg < MAX_MESSAGE_LEN);
408: DEBUGPRINT(("Set default message: '%s'\n", DefaultMessage.msg));
1.1.1.3 ! root 409: DefaultMessage.shown = false;
1.1 root 410: }
411:
412: /*-----------------------------------------------------------------------*/
413: /**
414: * Draw 'msg' centered to the message area
415: */
416: static void Statusbar_DrawMessage(SDL_Surface *surf, const char *msg)
417: {
418: int fontw, fonth, offset;
419: SDL_FillRect(surf, &MessageRect, GrayBg);
420: if (*msg) {
421: SDLGui_GetFontSize(&fontw, &fonth);
422: offset = (MessageRect.w - strlen(msg) * fontw) / 2;
423: SDLGui_Text(MessageRect.x + offset, MessageRect.y, msg);
424: }
425: SDL_UpdateRects(surf, 1, &MessageRect);
426: DEBUGPRINT(("Draw message: '%s'\n", msg));
427: }
428:
429: /*-----------------------------------------------------------------------*/
430: /**
431: * If message's not shown, show it. If message's timed out,
432: * remove it and show next one.
433: */
434: static void Statusbar_ShowMessage(SDL_Surface *surf, Uint32 ticks)
435: {
436: msg_item_t *next;
437:
438: if (MessageList->shown) {
439: if (!MessageList->expire) {
440: /* last/default message */
441: return;
442: }
443: if (MessageList->expire > ticks) {
444: /* not timed out yet */
445: return;
446: }
447: assert(MessageList->next); /* last message shouldn't end here */
448: next = MessageList->next;
449: free(MessageList);
450: MessageList = next;
451: /* make sure next message gets shown */
1.1.1.3 ! root 452: MessageList->shown = false;
1.1 root 453: }
454: if (!MessageList->shown) {
455: /* not shown yet, show */
456: Statusbar_DrawMessage(surf, MessageList->msg);
457: if (MessageList->timeout && !MessageList->expire) {
458: MessageList->expire = ticks + MessageList->timeout;
459: }
1.1.1.3 ! root 460: MessageList->shown = true;
1.1 root 461: }
462: }
463:
464:
465: /*-----------------------------------------------------------------------*/
466: /**
467: * Save the area that will be left under overlay led
468: */
469: void Statusbar_OverlayBackup(SDL_Surface *surf)
470: {
1.1.1.3 ! root 471: if ((StatusbarHeight && ConfigureParams.Screen.bShowStatusbar)
! 472: || !ConfigureParams.Screen.bShowDriveLed) {
1.1 root 473: /* overlay not used with statusbar */
474: return;
475: }
476: assert(surf);
477: if (!OverlayUnderside) {
478: SDL_Surface *bak;
479: SDL_PixelFormat *fmt = surf->format;
480: bak = SDL_CreateRGBSurface(surf->flags,
481: OverlayLedRect.w, OverlayLedRect.h,
482: fmt->BitsPerPixel,
483: fmt->Rmask, fmt->Gmask, fmt->Bmask,
484: fmt->Amask);
485: assert(bak);
486: OverlayUnderside = bak;
487: }
488: SDL_BlitSurface(surf, &OverlayLedRect, OverlayUnderside, NULL);
489: }
490:
491: /*-----------------------------------------------------------------------*/
492: /**
493: * Restore the area left under overlay led
494: */
495: void Statusbar_OverlayRestore(SDL_Surface *surf)
496: {
1.1.1.3 ! root 497: if ((StatusbarHeight && ConfigureParams.Screen.bShowStatusbar)
! 498: || !ConfigureParams.Screen.bShowDriveLed) {
1.1 root 499: /* overlay not used with statusbar */
500: return;
501: }
502: if (bOverlayState == OVERLAY_DRAWN && OverlayUnderside) {
503: assert(surf);
504: SDL_BlitSurface(OverlayUnderside, NULL, surf, &OverlayLedRect);
505: /* this will make the draw function to update this the screen */
506: bOverlayState = OVERLAY_RESTORED;
507: }
508: }
509:
510: /*-----------------------------------------------------------------------*/
511: /**
512: * Draw overlay led
513: */
514: static void Statusbar_OverlayDrawLed(SDL_Surface *surf, Uint32 color)
515: {
516: SDL_Rect rect;
517: if (bOverlayState == OVERLAY_DRAWN) {
518: /* some led already drawn */
519: return;
520: }
521: bOverlayState = OVERLAY_DRAWN;
522:
523: /* enabled led with border */
524: rect = OverlayLedRect;
525: rect.x += 1;
526: rect.y += 1;
527: rect.w -= 2;
528: rect.h -= 2;
529: SDL_FillRect(surf, &OverlayLedRect, LedColorBg);
530: SDL_FillRect(surf, &rect, color);
531: }
532:
533: /*-----------------------------------------------------------------------*/
534: /**
535: * Draw overlay led onto screen surface if any drives are enabled.
536: */
537: static void Statusbar_OverlayDraw(SDL_Surface *surf)
538: {
539: Uint32 currentticks = SDL_GetTicks();
540: int i;
541:
542: assert(surf);
543: if (bRecordingYM || bRecordingWav || bRecordingAnimation) {
544: Statusbar_OverlayDrawLed(surf, RecColorOn);
545: }
546: for (i = 0; i < MAX_DRIVE_LEDS; i++) {
547: if (Led[i].state) {
548: if (Led[i].expire && Led[i].expire < currentticks) {
1.1.1.3 ! root 549: Led[i].state = false;
1.1 root 550: continue;
551: }
552: Statusbar_OverlayDrawLed(surf, LedColorOn);
553: break;
554: }
555: }
556: /* possible state transitions:
557: * NONE -> DRAWN -> RESTORED -> DRAWN -> RESTORED -> NONE
558: * Other than NONE state needs to be updated on screen
559: */
560: switch (bOverlayState) {
561: case OVERLAY_RESTORED:
562: bOverlayState = OVERLAY_NONE;
563: case OVERLAY_DRAWN:
564: SDL_UpdateRects(surf, 1, &OverlayLedRect);
565: DEBUGPRINT(("Overlay LED = %s\n", bOverlayState==OVERLAY_DRAWN?"ON":"OFF"));
566: break;
567: case OVERLAY_NONE:
568: break;
569: }
570: }
571:
572:
573: /*-----------------------------------------------------------------------*/
574: /**
575: * Update statusbar information (leds etc) if/when needed.
576: *
577: * May not be called when screen is locked (SDL limitation).
578: */
579: void Statusbar_Update(SDL_Surface *surf)
580: {
581: Uint32 color, currentticks;
582: SDL_Rect rect;
583: int i;
584:
585: if (!(StatusbarHeight && ConfigureParams.Screen.bShowStatusbar)) {
586: /* not enabled (anymore), show overlay led instead? */
587: if (ConfigureParams.Screen.bShowDriveLed) {
588: Statusbar_OverlayDraw(surf);
589: }
590: return;
591: }
592: assert(surf);
593: /* Statusbar_Init() not called before this? */
594: assert(surf->h == ScreenHeight + StatusbarHeight);
595:
596: rect = LedRect;
597: currentticks = SDL_GetTicks();
598: for (i = 0; i < MAX_DRIVE_LEDS; i++) {
599: if (Led[i].expire && Led[i].expire < currentticks) {
1.1.1.3 ! root 600: Led[i].state = false;
1.1 root 601: }
602: if (Led[i].state == Led[i].oldstate) {
603: continue;
604: }
605: Led[i].oldstate = Led[i].state;
606: if (Led[i].state) {
607: color = LedColorOn;
608: } else {
609: color = LedColorOff;
610: }
611: rect.x = Led[i].offset;
612: SDL_FillRect(surf, &rect, color);
613: SDL_UpdateRects(surf, 1, &rect);
614: DEBUGPRINT(("LED[%d] = %s\n", i, Led[i].state?"ON":"OFF"));
615: }
616: Statusbar_ShowMessage(surf, currentticks);
617:
618: if (nFrameSkips != nOldFrameSkips) {
1.1.1.3 ! root 619: char fscount[2] = { '\0', '\0' };
! 620: if (nFrameSkips < 10)
! 621: fscount[0] = '0' + nFrameSkips;
! 622: else
! 623: fscount[0] = 'X';
1.1 root 624: SDL_FillRect(surf, &FrameSkipsRect, GrayBg);
625: SDLGui_Text(FrameSkipsRect.x, FrameSkipsRect.y, fscount);
626: SDL_UpdateRects(surf, 1, &FrameSkipsRect);
627: DEBUGPRINT(("FS = %s\n", fscount));
628: nOldFrameSkips = nFrameSkips;
629: }
630:
631: if ((bRecordingYM || bRecordingWav || bRecordingAnimation)
632: != bOldRecording) {
633: bOldRecording = !bOldRecording;
634: if (bOldRecording) {
635: color = RecColorOn;
636: } else {
637: color = RecColorOff;
638: }
639: SDL_FillRect(surf, &RecLedRect, color);
640: SDL_UpdateRects(surf, 1, &RecLedRect);
641: DEBUGPRINT(("REC = ON\n"));
642: }
643: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.