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