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