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