1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Fullscreen BeOS gfx driver based on BScreen.
12  *
13  *      By Jason Wilkins.
14  *
15  *      See readme.txt for copyright information.
16  */
17 
18 #include "bealleg.h"
19 #include "allegro/internal/aintern.h"
20 #include "allegro/platform/aintbeos.h"
21 
22 #if !defined ALLEGRO_BEOS && !defined ALLEGRO_HAIKU
23 #error something is wrong with the makefile
24 #endif
25 
26 
27 static char driver_desc[256] = EMPTY_STRING;
28 static thread_id palette_thread_id = -1;
29 static sem_id palette_sem = -1;
30 static rgb_color palette_colors[256];
31 
32 
33 
34 /* BeAllegroScreen::BeAllegroScreen:
35  *  Constructor, switches gfx mode and initializes the screen.
36  */
BeAllegroScreen(const char * title,uint32 space,status_t * error,bool debugging)37 BeAllegroScreen::BeAllegroScreen(const char *title, uint32 space, status_t *error, bool debugging)
38    : BWindowScreen(title, space, error, debugging)
39 {
40 }
41 
42 
43 
44 /* BeAllegroScreen::ScreenConnected:
45  *  Callback for when the user switches in or out of the workspace.
46  */
ScreenConnected(bool connected)47 void BeAllegroScreen::ScreenConnected(bool connected)
48 {
49    if(connected) {
50       _be_change_focus(connected);
51       release_sem(_be_fullscreen_lock);
52    }
53    else {
54       acquire_sem(_be_fullscreen_lock);
55       _be_change_focus(connected);
56    }
57 }
58 
59 
60 
61 /* BeAllegroScreen::QuitRequested:
62  *  User requested to close the program window.
63  */
QuitRequested(void)64 bool BeAllegroScreen::QuitRequested(void)
65 {
66    return _be_handle_window_close(Title());
67 }
68 
69 
70 
71 /* BeAllegroScreen::MessageReceived:
72  *  System messages handler.
73  */
MessageReceived(BMessage * message)74 void BeAllegroScreen::MessageReceived(BMessage *message)
75 {
76    switch (message->what) {
77       case B_SIMPLE_DATA:
78          break;
79 
80       case B_MOUSE_WHEEL_CHANGED:
81          float dy;
82          message->FindFloat("be:wheel_delta_y", &dy);
83          _be_mouse_z += ((int)dy > 0 ? -1 : 1);
84          break;
85 
86       default:
87          BWindowScreen::MessageReceived(message);
88          break;
89    }
90 }
91 
92 
93 
94 /* find_gfx_mode:
95  *  Searches given mode and returns the corresponding entry in the modes
96  *  table. Returns -1 if mode is unavailable.
97  */
find_gfx_mode(int w,int h,int d)98 static inline uint32 find_gfx_mode(int w, int h, int d)
99 {
100    int index = 0;
101    while (_be_mode_table[index].d > 0) {
102       if ((_be_mode_table[index].w == w) &&
103           (_be_mode_table[index].h == h) &&
104           (_be_mode_table[index].d == d))
105          return _be_mode_table[index].mode;
106       index++;
107    }
108 
109    return (uint32)-1;
110 }
111 
112 
113 
114 /* be_sort_out_virtual_resolution:
115  *  Computes and initializes framebuffer layout for given virtual
116  *  resolution.
117  */
be_sort_out_virtual_resolution(int w,int h,int * v_w,int * v_h,int color_depth)118 static inline bool be_sort_out_virtual_resolution(int w, int h, int *v_w, int *v_h, int color_depth)
119 {
120    int32 try_v_w;
121    int32 try_v_h;
122    // Possible VRAM amounts. Are these always powers of 2?
123    int ram_count[] = { 256, 128, 64, 32, 16, 8, 4, 2, 1, 0 };
124    int i;
125 
126    if (*v_w == 0)
127       try_v_w = MIN(w, 32767);
128    else
129       try_v_w = MIN(*v_w, 32767);
130    try_v_h = *v_h;
131 
132    if (*v_h == 0) {
133       for (i = 0; ram_count[i]; i++) {
134 	 try_v_h = (1024 * 1024 * ram_count[i]) / (try_v_w * BYTES_PER_PIXEL(color_depth));
135 	 /* Evil hack: under BeOS R5 SetFrameBuffer() should work with any
136 	  * int32 width and height parameters, but actually it crashes if
137 	  * one of them exceeds the boundaries of a signed short variable.
138 	  */
139 	 try_v_h = MIN(try_v_h, 32767);
140 
141 	 if (_be_allegro_screen->SetFrameBuffer(try_v_w, try_v_h) == B_OK) {
142 	    *v_w = try_v_w;
143 	    *v_h = try_v_h;
144 	    return true;
145          }
146       }
147       try_v_h = h;
148    }
149 
150    if (_be_allegro_screen->SetFrameBuffer(try_v_w, try_v_h) == B_OK) {
151       *v_w = try_v_w;
152       *v_h = try_v_h;
153       return true;
154    }
155    else {
156       return false;
157    }
158 }
159 
160 
161 
162 /* palette_updater_thread:
163  *  This small thread is used to update the palette in fullscreen mode. It may seem
164  *  unnecessary as a direct call to SetColorList would do it, but calling directly
165  *  this function from the main thread has proven to be a major bottleneck, so
166  *  calling it from a separated thread is a good thing.
167  */
palette_updater_thread(void * data)168 static int32 palette_updater_thread(void *data)
169 {
170    BeAllegroScreen *s = (BeAllegroScreen *)data;
171 
172    while (_be_gfx_initialized) {
173       acquire_sem(palette_sem);
174       s->SetColorList(palette_colors, 0, 255);
175    }
176    return B_OK;
177 }
178 
179 
180 
181 /* be_gfx_bwindowscreen_fetch_mode_list:
182  *  Builds the list of available video modes.
183  */
be_gfx_bwindowscreen_fetch_mode_list(void)184 extern "C" struct GFX_MODE_LIST *be_gfx_bwindowscreen_fetch_mode_list(void)
185 {
186    int j, be_mode, num_modes = 0;
187    uint32 i, count;
188    display_mode *mode;
189    GFX_MODE_LIST *mode_list;
190    bool already_there;
191 
192    if (BScreen().GetModeList(&mode, &count) != B_OK)
193       return NULL;
194 
195    mode_list = (GFX_MODE_LIST *)malloc(sizeof(GFX_MODE_LIST));
196    if (!mode_list) {
197       free(mode);
198       return NULL;
199    }
200    mode_list->mode = NULL;
201 
202    for (i=0; i<count; i++) {
203       be_mode = 0;
204       while (_be_mode_table[be_mode].d > 0) {
205          if ((mode[i].virtual_width == _be_mode_table[be_mode].w) &&
206              (mode[i].virtual_height == _be_mode_table[be_mode].h) &&
207              (mode[i].space == _be_mode_table[be_mode].space))
208             break;
209          be_mode++;
210       }
211       if (_be_mode_table[be_mode].d == -1)
212          continue;
213 
214       already_there = false;
215       for (j=0; j<num_modes; j++) {
216          if ((mode_list->mode[j].width == _be_mode_table[be_mode].w) &&
217              (mode_list->mode[j].height == _be_mode_table[be_mode].h) &&
218              (mode_list->mode[j].bpp == _be_mode_table[be_mode].d)) {
219             already_there = true;
220             break;
221          }
222       }
223       if (!already_there) {
224          mode_list->mode = (GFX_MODE *)realloc(mode_list->mode, sizeof(GFX_MODE) * (num_modes + 1));
225          if (!mode_list->mode) {
226             free(mode);
227             return NULL;
228          }
229          mode_list->mode[num_modes].width = _be_mode_table[be_mode].w;
230          mode_list->mode[num_modes].height = _be_mode_table[be_mode].h;
231          mode_list->mode[num_modes].bpp = _be_mode_table[be_mode].d;
232          num_modes++;
233       }
234    }
235    mode_list->mode = (GFX_MODE *)realloc(mode_list->mode, sizeof(GFX_MODE) * (num_modes + 1));
236    if (!mode_list->mode) {
237       free(mode);
238       return NULL;
239    }
240    mode_list->mode[num_modes].width = 0;
241    mode_list->mode[num_modes].height = 0;
242    mode_list->mode[num_modes].bpp = 0;
243    mode_list->num_modes = num_modes;
244 
245    free(mode);
246 
247    return mode_list;
248 }
249 
250 
251 
252 /* _be_gfx_bwindowscreen_init:
253  *  Initializes the driver for given gfx mode.
254  */
_be_gfx_bwindowscreen_init(GFX_DRIVER * drv,int w,int h,int v_w,int v_h,int color_depth,bool accel)255 static struct BITMAP *_be_gfx_bwindowscreen_init(GFX_DRIVER *drv, int w, int h, int v_w, int v_h, int color_depth, bool accel)
256 {
257    BITMAP             *bmp;
258    status_t            error;
259    uint32              mode;
260    graphics_card_info *gfx_card;
261    frame_buffer_info  *fbuffer;
262    accelerant_device_info info;
263    char tmp1[128], tmp2[128];
264 
265    if (1
266 #ifdef ALLEGRO_COLOR8
267        && (color_depth != 8)
268 #endif
269 #ifdef ALLEGRO_COLOR16
270        && (color_depth != 15)
271        && (color_depth != 16)
272 #endif
273 #ifdef ALLEGRO_COLOR24
274        && (color_depth != 24)
275 #endif
276 #ifdef ALLEGRO_COLOR32
277        && (color_depth != 32)
278 #endif
279        ) {
280       ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Unsupported color depth"));
281       return NULL;
282    }
283 
284    if ((w == 0) && (h == 0)) {
285       w = 640;
286       h = 480;
287    }
288    mode = find_gfx_mode(w, h, color_depth);
289 
290    if (mode == (uint32)-1) {
291       goto cleanup;
292    }
293 
294    _be_fullscreen_lock = create_sem(0, "screen lock");
295 
296    if (_be_fullscreen_lock < 0) {
297       goto cleanup;
298    }
299 
300    _be_lock_count = 0;
301 
302    set_display_switch_mode(SWITCH_AMNESIA);
303 
304    _be_allegro_screen = new BeAllegroScreen(wnd_title, mode, &error, false);
305    _be_window = _be_allegro_screen;
306 
307    if(error != B_OK) {
308       ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Resolution not supported"));
309       goto cleanup;
310    }
311 
312    _be_mouse_view = new BView(_be_allegro_screen->Bounds(),
313      "allegro mouse view", B_FOLLOW_ALL_SIDES, 0);
314    _be_allegro_screen->Lock();
315    _be_allegro_screen->AddChild(_be_mouse_view);
316    _be_allegro_screen->Unlock();
317 
318    _be_mouse_window = _be_allegro_screen;
319    _be_mouse_window_mode = false;
320    _mouse_on = TRUE;
321 
322    release_sem(_be_mouse_view_attached);
323 
324    palette_sem = create_sem(0, "palette sem");
325    if (palette_sem < 0) {
326       goto cleanup;
327    }
328 
329    _be_gfx_initialized = false;
330    _be_allegro_screen->Show();
331    acquire_sem(_be_fullscreen_lock);
332 
333    gfx_card = _be_allegro_screen->CardInfo();
334 
335    if (!be_sort_out_virtual_resolution(w, h, &v_w, &v_h, color_depth)) {
336       v_w = w;
337       v_h = h;
338    }
339 
340    /* BWindowScreen sets refresh rate at 60 Hz by default */
341    _set_current_refresh_rate(60);
342 
343    fbuffer = _be_allegro_screen->FrameBufferInfo();
344 
345    drv->w       = w;
346    drv->h       = h;
347    drv->linear  = TRUE;
348    drv->vid_mem = v_w * v_h * BYTES_PER_PIXEL(color_depth);
349 
350    bmp = _make_bitmap(fbuffer->width, fbuffer->height,
351             (unsigned long)gfx_card->frame_buffer, drv,
352             color_depth, fbuffer->bytes_per_row);
353 
354    if (!bmp) {
355       ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Not enough memory"));
356       goto cleanup;
357    }
358 
359    _be_sync_func = NULL;
360    if (accel) {
361       be_gfx_bwindowscreen_accelerate(color_depth);
362    }
363 
364 #ifdef ALLEGRO_NO_ASM
365    if (gfx_capabilities) {
366       bmp->write_bank = (void *)be_gfx_bwindowscreen_read_write_bank;
367       bmp->read_bank  = (void *)be_gfx_bwindowscreen_read_write_bank;
368       _screen_vtable.unwrite_bank = (void *)be_gfx_bwindowscreen_unwrite_bank;
369    }
370 #else
371    if (gfx_capabilities) {
372       bmp->write_bank = _be_gfx_bwindowscreen_read_write_bank_asm;
373       bmp->read_bank  = _be_gfx_bwindowscreen_read_write_bank_asm;
374       _screen_vtable.unwrite_bank = _be_gfx_bwindowscreen_unwrite_bank_asm;
375    }
376 #endif
377    gfx_capabilities |= GFX_CAN_TRIPLE_BUFFER;
378 
379    _screen_vtable.acquire      = be_gfx_bwindowscreen_acquire;
380    _screen_vtable.release      = be_gfx_bwindowscreen_release;
381 
382    _be_gfx_set_truecolor_shifts();
383    if (BScreen().GetDeviceInfo(&info) == B_OK)
384       uszprintf(driver_desc, sizeof(driver_desc), uconvert_ascii("BWindowScreen object (%s)", tmp1),
385                 uconvert_ascii(info.name, tmp2));
386    else
387       ustrzcpy(driver_desc, sizeof(driver_desc), uconvert_ascii("BWindowScreen object", tmp1));
388    drv->desc = driver_desc;
389 
390    _be_gfx_initialized = true;
391 
392    palette_thread_id = spawn_thread(palette_updater_thread, "palette updater",
393 				    B_DISPLAY_PRIORITY, (void *)_be_allegro_screen);
394    resume_thread(palette_thread_id);
395 
396    be_app->HideCursor();
397 
398    release_sem(_be_fullscreen_lock);
399    while (!_be_focus_count);
400 
401    return bmp;
402 
403    cleanup: {
404       be_gfx_bwindowscreen_exit(NULL);
405       return NULL;
406    }
407 }
408 
409 
410 
be_gfx_bwindowscreen_accel_init(int w,int h,int v_w,int v_h,int color_depth)411 extern "C" struct BITMAP *be_gfx_bwindowscreen_accel_init(int w, int h, int v_w, int v_h, int color_depth)
412 {
413    return _be_gfx_bwindowscreen_init(&gfx_beos_bwindowscreen_accel, w, h, v_w, v_h, color_depth, true);
414 }
415 
416 
417 
be_gfx_bwindowscreen_init(int w,int h,int v_w,int v_h,int color_depth)418 extern "C" struct BITMAP *be_gfx_bwindowscreen_init(int w, int h, int v_w, int v_h, int color_depth)
419 {
420    return _be_gfx_bwindowscreen_init(&gfx_beos_bwindowscreen, w, h, v_w, v_h, color_depth, false);
421 }
422 
423 
424 
425 /* be_gfx_bwindowscreen_exit:
426  *  Shuts down the driver.
427  */
be_gfx_bwindowscreen_exit(struct BITMAP * bmp)428 extern "C" void be_gfx_bwindowscreen_exit(struct BITMAP *bmp)
429 {
430    status_t ret_value;
431    (void)bmp;
432 
433    if (_be_fullscreen_lock > 0) {
434       delete_sem(_be_fullscreen_lock);
435       _be_fullscreen_lock = -1;
436    }
437    _be_gfx_initialized = false;
438 
439    if (palette_thread_id >= 0) {
440       release_sem(palette_sem);
441       wait_for_thread(palette_thread_id, &ret_value);
442       palette_thread_id = -1;
443    }
444    if (palette_sem >= 0) {
445       delete_sem(palette_sem);
446       palette_sem = -1;
447    }
448 
449    if (_be_allegro_screen != NULL) {
450       if (_be_mouse_view_attached < 1) {
451 	 acquire_sem(_be_mouse_view_attached);
452       }
453 
454       _be_allegro_screen->Lock();
455       _be_allegro_screen->Quit();
456 
457       _be_allegro_screen = NULL;
458       _be_window = NULL;
459    }
460 
461    be_app->ShowCursor();
462 
463    _be_mouse_window   = NULL;
464    _be_mouse_view     = NULL;
465 
466    _be_focus_count = 0;
467    _be_lock_count = 0;
468 }
469 
470 
471 
472 #ifdef ALLEGRO_NO_ASM
473 
474 /* be_gfx_bwindowscreen_read_write_bank:
475  *  Returns new line and synchronizes framebuffer if needed.
476  */
be_gfx_bwindowscreen_read_write_bank(BITMAP * bmp,int line)477 extern "C" uintptr_t be_gfx_bwindowscreen_read_write_bank(BITMAP *bmp, int line)
478 {
479    if (!(bmp->id & BMP_ID_LOCKED)) {
480       _be_sync_func();
481       bmp->id |= (BMP_ID_LOCKED | BMP_ID_AUTOLOCK);
482    }
483    return (unsigned long)(bmp->line[line]);
484 }
485 
486 
487 
488 /* be_gfx_bwindowscreen_unwrite_bank:
489  *  Unlocks bitmap if necessary.
490  */
be_gfx_bwindowscreen_unwrite_bank(BITMAP * bmp)491 extern "C" void be_gfx_bwindowscreen_unwrite_bank(BITMAP *bmp)
492 {
493    if (bmp->id & BMP_ID_AUTOLOCK) {
494       bmp->id &= ~(BMP_ID_LOCKED | BMP_ID_AUTOLOCK);
495    }
496 }
497 
498 #endif
499 
500 
501 
502 /* be_gfx_bwindowscreen_acquire:
503  *  Locks specified video bitmap.
504  */
be_gfx_bwindowscreen_acquire(struct BITMAP * bmp)505 extern "C" void be_gfx_bwindowscreen_acquire(struct BITMAP *bmp)
506 {
507    if (_be_lock_count == 0) {
508       acquire_sem(_be_fullscreen_lock);
509       bmp->id |= BMP_ID_LOCKED;
510    }
511    if (_be_sync_func)
512       _be_sync_func();
513    _be_lock_count++;
514 }
515 
516 
517 
518 /* be_gfx_bwindowscreen_release:
519  *  Unlocks specified video bitmap.
520  */
be_gfx_bwindowscreen_release(struct BITMAP * bmp)521 extern "C" void be_gfx_bwindowscreen_release(struct BITMAP *bmp)
522 {
523    _be_lock_count--;
524 
525    if (_be_lock_count == 0) {
526       bmp->id &= ~BMP_ID_LOCKED;
527       release_sem(_be_fullscreen_lock);
528    }
529 }
530 
531 
532 
533 /* be_gfx_bwindowscreen_set_palette:
534  *  Sets the palette colors and notices the palette updater thread
535  *  about the change.
536  */
be_gfx_bwindowscreen_set_palette(AL_CONST struct RGB * p,int from,int to,int vsync)537 extern "C" void be_gfx_bwindowscreen_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync)
538 {
539    if (vsync)
540       be_gfx_vsync();
541 
542    for(int index = from; index <= to; index++) {
543       palette_colors[index].red   = _rgb_scale_6[p[index].r];
544       palette_colors[index].green = _rgb_scale_6[p[index].g];
545       palette_colors[index].blue  = _rgb_scale_6[p[index].b];
546       palette_colors[index].alpha = 255;
547    }
548    /* Update palette! */
549    release_sem(palette_sem);
550 }
551 
552 
553 
554 /* be_gfx_bwindowscreen_scroll:
555  *  Scrolls the visible viewport.
556  */
be_gfx_bwindowscreen_scroll(int x,int y)557 extern "C" int be_gfx_bwindowscreen_scroll(int x, int y)
558 {
559    int rv;
560 
561    acquire_screen();
562 
563    if (_be_allegro_screen->MoveDisplayArea(x, y) != B_ERROR) {
564       rv = 0;
565    }
566    else {
567       rv = 1;
568    }
569 
570    release_screen();
571 
572    if (_wait_for_vsync)
573       be_gfx_vsync();
574 
575    return rv;
576 }
577 
578 
579 
580 /* be_gfx_bwindowscreen_poll_scroll:
581  *  Returns true if there are pending scrolling requests left.
582  */
be_gfx_bwindowscreen_poll_scroll(void)583 extern "C" int be_gfx_bwindowscreen_poll_scroll(void)
584 {
585    return (BScreen(_be_allegro_screen).WaitForRetrace(0) == B_ERROR ? TRUE : FALSE);
586 }
587 
588 
589 
590 /* be_gfx_bwindowscreen_request_scroll:
591  *  Starts a screen scroll but doesn't wait for the retrace.
592  */
be_gfx_bwindowscreen_request_scroll(int x,int y)593 extern "C" int be_gfx_bwindowscreen_request_scroll(int x, int y)
594 {
595    acquire_screen();
596    _be_allegro_screen->MoveDisplayArea(x, y);
597    release_screen();
598 
599    return 0;
600 }
601 
602 
603 
604 /* be_gfx_bwindowscreen_request_video_bitmap:
605  *  Page flips to display specified bitmap, but doesn't wait for retrace.
606  */
be_gfx_bwindowscreen_request_video_bitmap(struct BITMAP * bmp)607 extern "C" int be_gfx_bwindowscreen_request_video_bitmap(struct BITMAP *bmp)
608 {
609    int rv;
610 
611    acquire_screen();
612    rv = _be_allegro_screen->MoveDisplayArea(bmp->x_ofs, bmp->y_ofs) == B_ERROR ? 1 : 0;
613    release_screen();
614 
615    return rv;
616 }
617 
618