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