rubidium@10379: /* $Id$ */ rubidium@10379: rubidium@10379: /** @file allegro_v.cpp Implementation of the Allegro video driver. */ rubidium@10379: rubidium@10379: #ifdef WITH_ALLEGRO rubidium@10379: rubidium@10379: #include "../stdafx.h" rubidium@10379: #include "../openttd.h" rubidium@10379: #include "../debug.h" rubidium@10379: #include "../gfx_func.h" rubidium@10379: #include "../sdl.h" rubidium@10379: #include "../variables.h" rubidium@10379: #include "../rev.h" rubidium@10379: #include "../blitter/factory.hpp" rubidium@10379: #include "../network/network.h" rubidium@10379: #include "../core/math_func.hpp" rubidium@10379: #include "../core/random_func.hpp" rubidium@10379: #include "../functions.h" rubidium@10379: #include "../texteff.hpp" rubidium@10379: #include "allegro_v.h" rubidium@10379: #include rubidium@10379: rubidium@10379: static FVideoDriver_Allegro iFVideoDriver_Allegro; rubidium@10379: rubidium@10379: static BITMAP *_allegro_screen; rubidium@10379: rubidium@10379: #define MAX_DIRTY_RECTS 100 rubidium@10379: static PointDimension _dirty_rects[MAX_DIRTY_RECTS]; rubidium@10379: static int _num_dirty_rects; rubidium@10379: rubidium@10379: void VideoDriver_Allegro::MakeDirty(int left, int top, int width, int height) rubidium@10379: { rubidium@10379: if (_num_dirty_rects < MAX_DIRTY_RECTS) { rubidium@10379: _dirty_rects[_num_dirty_rects].x = left; rubidium@10379: _dirty_rects[_num_dirty_rects].y = top; rubidium@10379: _dirty_rects[_num_dirty_rects].width = width; rubidium@10379: _dirty_rects[_num_dirty_rects].height = height; rubidium@10379: } rubidium@10379: _num_dirty_rects++; rubidium@10379: } rubidium@10379: rubidium@10379: static void DrawSurfaceToScreen() rubidium@10379: { rubidium@10379: int n = _num_dirty_rects; rubidium@10379: if (n == 0) return; rubidium@10379: rubidium@10379: _num_dirty_rects = 0; rubidium@10379: if (n > MAX_DIRTY_RECTS) { rubidium@10379: blit(_allegro_screen, screen, 0, 0, 0, 0, _allegro_screen->w, _allegro_screen->h); rubidium@10379: return; rubidium@10379: } rubidium@10379: rubidium@10379: for (int i = 0; i < n; i++) { rubidium@10379: blit(_allegro_screen, screen, _dirty_rects[i].x, _dirty_rects[i].y, _dirty_rects[i].x, _dirty_rects[i].y, _dirty_rects[i].width, _dirty_rects[i].height); rubidium@10379: } rubidium@10379: } rubidium@10379: rubidium@10379: rubidium@10379: static void UpdatePalette(uint start, uint count) rubidium@10379: { rubidium@10379: static PALETTE pal; rubidium@10379: rubidium@10379: uint end = start + count; rubidium@10379: for (uint i = start; i != end; i++) { rubidium@10379: pal[i].r = _cur_palette[i].r / 4; rubidium@10379: pal[i].g = _cur_palette[i].g / 4; rubidium@10379: pal[i].b = _cur_palette[i].b / 4; rubidium@10379: pal[i].filler = 0; rubidium@10379: } rubidium@10379: rubidium@10379: set_palette_range(pal, start, end - 1, 1); rubidium@10379: } rubidium@10379: rubidium@10379: static void InitPalette() rubidium@10379: { rubidium@10379: UpdatePalette(0, 256); rubidium@10379: } rubidium@10379: rubidium@10379: static void CheckPaletteAnim() rubidium@10379: { rubidium@10379: if (_pal_count_dirty != 0) { rubidium@10379: Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); rubidium@10379: rubidium@10379: switch (blitter->UsePaletteAnimation()) { rubidium@10379: case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND: rubidium@10379: UpdatePalette(_pal_first_dirty, _pal_count_dirty); rubidium@10379: break; rubidium@10379: rubidium@10379: case Blitter::PALETTE_ANIMATION_BLITTER: rubidium@10379: blitter->PaletteAnimate(_pal_first_dirty, _pal_count_dirty); rubidium@10379: break; rubidium@10379: rubidium@10379: case Blitter::PALETTE_ANIMATION_NONE: rubidium@10379: break; rubidium@10379: rubidium@10379: default: rubidium@10379: NOT_REACHED(); rubidium@10379: } rubidium@10379: _pal_count_dirty = 0; rubidium@10379: } rubidium@10379: } rubidium@10379: rubidium@10379: static const Dimension default_resolutions[] = { rubidium@10379: { 640, 480}, rubidium@10379: { 800, 600}, rubidium@10379: {1024, 768}, rubidium@10379: {1152, 864}, rubidium@10379: {1280, 800}, rubidium@10379: {1280, 960}, rubidium@10379: {1280, 1024}, rubidium@10379: {1400, 1050}, rubidium@10379: {1600, 1200}, rubidium@10379: {1680, 1050}, rubidium@10379: {1920, 1200} rubidium@10379: }; rubidium@10379: rubidium@10379: static void GetVideoModes() rubidium@10379: { rubidium@10379: /* Need to set a gfx_mode as there is NO other way to autodetect for rubidium@10379: * cards ourselves... and we need a card to get the modes. */ rubidium@10379: set_gfx_mode(_fullscreen ? GFX_AUTODETECT_FULLSCREEN : GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); rubidium@10379: rubidium@10379: GFX_MODE_LIST *mode_list = get_gfx_mode_list(gfx_driver->id); rubidium@10379: if (mode_list == NULL) { rubidium@10379: memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); rubidium@10379: _num_resolutions = lengthof(default_resolutions); rubidium@10379: return; rubidium@10379: } rubidium@10379: rubidium@10379: GFX_MODE *modes = mode_list->mode; rubidium@10379: rubidium@10379: int n = 0; rubidium@10379: for (int i = 0; modes[i].bpp != 0; i++) { rubidium@10379: int w = modes[i].width; rubidium@10379: int h = modes[i].height; rubidium@10379: if (w >= 640 && h >= 480) { rubidium@10379: int j; rubidium@10379: for (j = 0; j < n; j++) { rubidium@10379: if (_resolutions[j].width == w && _resolutions[j].height == h) break; rubidium@10379: } rubidium@10379: rubidium@10379: if (j == n) { rubidium@10379: _resolutions[j].width = w; rubidium@10379: _resolutions[j].height = h; rubidium@10379: if (++n == lengthof(_resolutions)) break; rubidium@10379: } rubidium@10379: } rubidium@10379: } rubidium@10379: _num_resolutions = n; rubidium@10379: SortResolutions(_num_resolutions); rubidium@10379: rubidium@10379: destroy_gfx_mode_list(mode_list); rubidium@10379: } rubidium@10379: rubidium@10379: static void GetAvailableVideoMode(int *w, int *h) rubidium@10379: { rubidium@10379: /* is the wanted mode among the available modes? */ rubidium@10379: for (int i = 0; i != _num_resolutions; i++) { rubidium@10379: if (*w == _resolutions[i].width && *h == _resolutions[i].height) return; rubidium@10379: } rubidium@10379: rubidium@10379: /* use the closest possible resolution */ rubidium@10379: int best = 0; rubidium@10379: uint delta = abs((_resolutions[0].width - *w) * (_resolutions[0].height - *h)); rubidium@10379: for (int i = 1; i != _num_resolutions; ++i) { rubidium@10379: uint newdelta = abs((_resolutions[i].width - *w) * (_resolutions[i].height - *h)); rubidium@10379: if (newdelta < delta) { rubidium@10379: best = i; rubidium@10379: delta = newdelta; rubidium@10379: } rubidium@10379: } rubidium@10379: *w = _resolutions[best].width; rubidium@10379: *h = _resolutions[best].height; rubidium@10379: } rubidium@10379: rubidium@10379: static bool CreateMainSurface(int w, int h) rubidium@10379: { rubidium@10379: int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); rubidium@10379: if (bpp == 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals"); rubidium@10379: set_color_depth(bpp); rubidium@10379: rubidium@10379: GetVideoModes(); rubidium@10379: GetAvailableVideoMode(&w, &h); rubidium@10379: if (set_gfx_mode(_fullscreen ? GFX_AUTODETECT_FULLSCREEN : GFX_AUTODETECT_WINDOWED, w, h, 0, 0) != 0) return false; rubidium@10379: rubidium@10379: _allegro_screen = create_bitmap(screen->w, screen->h); rubidium@10379: _screen.width = screen->w; rubidium@10379: _screen.height = screen->h; rubidium@10379: _screen.pitch = ((byte*)screen->line[1] - (byte*)screen->line[0]) / (bitmap_color_depth(screen) / 8); rubidium@10379: rubidium@10379: poll_mouse(); rubidium@10379: _cursor.pos.x = mouse_x; rubidium@10379: _cursor.pos.y = mouse_y; rubidium@10379: rubidium@10379: InitPalette(); rubidium@10379: rubidium@10379: char caption[32]; rubidium@10379: snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); rubidium@10379: set_window_title(caption); rubidium@10379: rubidium@10379: GameSizeChanged(); rubidium@10379: rubidium@10379: return true; rubidium@10379: } rubidium@10379: rubidium@10379: struct VkMapping { rubidium@10379: uint16 vk_from; rubidium@10379: byte vk_count; rubidium@10379: byte map_to; rubidium@10379: }; rubidium@10379: rubidium@10379: #define AS(x, z) {x, 0, z} rubidium@10379: #define AM(x, y, z, w) {x, y - x, z} rubidium@10379: rubidium@10379: static const VkMapping _vk_mapping[] = { rubidium@10379: /* Pageup stuff + up/down */ rubidium@10379: AM(KEY_PGUP, KEY_PGDN, WKC_PAGEUP, WKC_PAGEDOWN), rubidium@10379: AS(KEY_UP, WKC_UP), rubidium@10379: AS(KEY_DOWN, WKC_DOWN), rubidium@10379: AS(KEY_LEFT, WKC_LEFT), rubidium@10379: AS(KEY_RIGHT, WKC_RIGHT), rubidium@10379: rubidium@10379: AS(KEY_HOME, WKC_HOME), rubidium@10379: AS(KEY_END, WKC_END), rubidium@10379: rubidium@10379: AS(KEY_INSERT, WKC_INSERT), rubidium@10379: AS(KEY_DEL, WKC_DELETE), rubidium@10379: rubidium@10379: /* Map letters & digits */ rubidium@10379: AM(KEY_A, KEY_Z, 'A', 'Z'), rubidium@10379: AM(KEY_0, KEY_9, '0', '9'), rubidium@10379: rubidium@10379: AS(KEY_ESC, WKC_ESC), rubidium@10379: AS(KEY_PAUSE, WKC_PAUSE), rubidium@10379: AS(KEY_BACKSPACE, WKC_BACKSPACE), rubidium@10379: rubidium@10379: AS(KEY_SPACE, WKC_SPACE), rubidium@10379: AS(KEY_ENTER, WKC_RETURN), rubidium@10379: AS(KEY_TAB, WKC_TAB), rubidium@10379: rubidium@10379: /* Function keys */ rubidium@10379: AM(KEY_F1, KEY_F12, WKC_F1, WKC_F12), rubidium@10379: rubidium@10379: /* Numeric part. */ rubidium@10379: AM(KEY_0_PAD, KEY_9_PAD, '0', '9'), rubidium@10379: AS(KEY_SLASH_PAD, WKC_NUM_DIV), rubidium@10379: AS(KEY_ASTERISK, WKC_NUM_MUL), rubidium@10379: AS(KEY_MINUS_PAD, WKC_NUM_MINUS), rubidium@10379: AS(KEY_PLUS_PAD, WKC_NUM_PLUS), rubidium@10379: AS(KEY_ENTER_PAD, WKC_NUM_ENTER), rubidium@10379: AS(KEY_DEL_PAD, WKC_DELETE), rubidium@10379: rubidium@10379: /* Other non-letter keys */ rubidium@10379: AS(KEY_SLASH, WKC_SLASH), rubidium@10379: AS(KEY_SEMICOLON, WKC_SEMICOLON), rubidium@10379: AS(KEY_EQUALS, WKC_EQUALS), rubidium@10379: AS(KEY_OPENBRACE, WKC_L_BRACKET), rubidium@10379: AS(KEY_BACKSLASH, WKC_BACKSLASH), rubidium@10379: AS(KEY_CLOSEBRACE, WKC_R_BRACKET), rubidium@10379: rubidium@10379: AS(KEY_QUOTE, WKC_SINGLEQUOTE), rubidium@10379: AS(KEY_COMMA, WKC_COMMA), rubidium@10379: AS(KEY_MINUS, WKC_MINUS), rubidium@10379: AS(KEY_STOP, WKC_PERIOD), rubidium@10379: AS(KEY_TILDE, WKC_BACKQUOTE), rubidium@10379: }; rubidium@10379: rubidium@10379: static uint32 ConvertAllegroKeyIntoMy() rubidium@10379: { rubidium@10379: int scancode; rubidium@10379: int unicode = ureadkey(&scancode); rubidium@10379: rubidium@10379: const VkMapping *map; rubidium@10379: uint key = 0; rubidium@10379: rubidium@10379: for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { rubidium@10379: if ((uint)(scancode - map->vk_from) <= map->vk_count) { rubidium@10379: key = scancode - map->vk_from + map->map_to; rubidium@10379: break; rubidium@10379: } rubidium@10379: } rubidium@10379: rubidium@10379: if (key_shifts & KB_SHIFT_FLAG) key |= WKC_SHIFT; rubidium@10379: if (key_shifts & KB_CTRL_FLAG) key |= WKC_CTRL; rubidium@10379: if (key_shifts & KB_ALT_FLAG) key |= WKC_ALT; rubidium@10379: #if 0 rubidium@10379: DEBUG(driver, 0, "Scancode character pressed %u", scancode); rubidium@10379: DEBUG(driver, 0, "Unicode character pressed %u", unicode); rubidium@10379: #endif rubidium@10379: return (key << 16) + unicode; rubidium@10379: } rubidium@10379: rubidium@10379: enum { rubidium@10379: LEFT_BUTTON, rubidium@10379: RIGHT_BUTTON, rubidium@10379: }; rubidium@10379: rubidium@10379: static void PollEvent() rubidium@10379: { rubidium@10379: poll_mouse(); rubidium@10379: rubidium@10379: bool mouse_action = false; rubidium@10379: rubidium@10379: /* Mouse buttons */ rubidium@10379: static int prev_button_state; rubidium@10379: if (prev_button_state != mouse_b) { rubidium@10379: uint diff = prev_button_state ^ mouse_b; rubidium@10379: while (diff != 0) { rubidium@10379: int button = FindFirstBit(diff); rubidium@10379: ClrBit(diff, button); rubidium@10379: if (HasBit(mouse_b, button)) { rubidium@10379: /* Pressed mouse button */ rubidium@10379: if (_rightclick_emulate && key_shifts & KB_CTRL_FLAG) { rubidium@10379: button = RIGHT_BUTTON; rubidium@10379: ClrBit(diff, RIGHT_BUTTON); rubidium@10379: } rubidium@10379: switch (button) { rubidium@10379: case LEFT_BUTTON: rubidium@10379: _left_button_down = true; rubidium@10379: break; rubidium@10379: rubidium@10379: case RIGHT_BUTTON: rubidium@10379: _right_button_down = true; rubidium@10379: _right_button_clicked = true; rubidium@10379: break; rubidium@10379: rubidium@10379: default: rubidium@10379: /* ignore rest */ rubidium@10379: break; rubidium@10379: } rubidium@10379: } else { rubidium@10379: /* Released mouse button */ rubidium@10379: if (_rightclick_emulate) { rubidium@10379: _right_button_down = false; rubidium@10379: _left_button_down = false; rubidium@10379: _left_button_clicked = false; rubidium@10379: } else if (button == LEFT_BUTTON) { rubidium@10379: _left_button_down = false; rubidium@10379: _left_button_clicked = false; rubidium@10379: } else if (button == RIGHT_BUTTON) { rubidium@10379: _right_button_down = false; rubidium@10379: } rubidium@10379: } rubidium@10379: } rubidium@10379: prev_button_state = mouse_b; rubidium@10379: mouse_action = true; rubidium@10379: } rubidium@10379: rubidium@10379: /* Mouse movement */ rubidium@10379: int dx = mouse_x - _cursor.pos.x; rubidium@10379: int dy = mouse_y - _cursor.pos.y; rubidium@10379: if (dx != 0 || dy != 0) { rubidium@10379: if (_cursor.fix_at) { rubidium@10379: _cursor.delta.x += dx; rubidium@10379: _cursor.delta.y += dy; rubidium@10379: position_mouse(_cursor.pos.x, _cursor.pos.y); rubidium@10379: } else { rubidium@10379: _cursor.delta.x = dx; rubidium@10379: _cursor.delta.y = dy; rubidium@10379: _cursor.pos.x = mouse_x; rubidium@10379: _cursor.pos.y = mouse_y; rubidium@10379: _cursor.dirty = true; rubidium@10379: } rubidium@10379: mouse_action = true; rubidium@10379: } rubidium@10379: rubidium@10379: if (mouse_action) HandleMouseEvents(); rubidium@10379: rubidium@10379: poll_keyboard(); rubidium@10379: if (key_shifts & KB_ALT_FLAG && (key[KEY_ENTER] || key[KEY_F])) { rubidium@10379: ToggleFullScreen(!_fullscreen); rubidium@10379: } else if (keypressed()) { rubidium@10379: HandleKeypress(ConvertAllegroKeyIntoMy()); rubidium@10379: } rubidium@10379: } rubidium@10379: rubidium@10380: /** There are multiple modules that might be using Allegro and rubidium@10380: * Allegro can only be initiated once. */ rubidium@10381: int _allegro_instance_count = 0; rubidium@10380: rubidium@10379: const char *VideoDriver_Allegro::Start(const char * const *parm) rubidium@10379: { rubidium@10381: if (_allegro_instance_count == 0 && install_allegro(SYSTEM_AUTODETECT, &errno, NULL)) return NULL; rubidium@10381: _allegro_instance_count++; rubidium@10379: rubidium@10379: install_timer(); rubidium@10379: install_mouse(); rubidium@10379: install_keyboard(); rubidium@10379: rubidium@10379: CreateMainSurface(_cur_resolution.width, _cur_resolution.height); rubidium@10379: MarkWholeScreenDirty(); rubidium@10379: set_close_button_callback(HandleExitGameRequest); rubidium@10379: rubidium@10379: return NULL; rubidium@10379: } rubidium@10379: rubidium@10379: void VideoDriver_Allegro::Stop() rubidium@10379: { rubidium@10381: if (--_allegro_instance_count == 0) allegro_exit(); rubidium@10379: } rubidium@10379: rubidium@10379: #if defined(UNIX) || defined(__OS2__) || defined(PSP) rubidium@10379: # include /* gettimeofday */ rubidium@10379: rubidium@10379: static uint32 GetTime() rubidium@10379: { rubidium@10379: struct timeval tim; rubidium@10379: rubidium@10379: gettimeofday(&tim, NULL); rubidium@10379: return tim.tv_usec / 1000 + tim.tv_sec * 1000; rubidium@10379: } rubidium@10379: #else rubidium@10379: static uint32 GetTime() rubidium@10379: { rubidium@10379: return GetTickCount(); rubidium@10379: } rubidium@10379: #endif rubidium@10379: rubidium@10379: rubidium@10379: void VideoDriver_Allegro::MainLoop() rubidium@10379: { rubidium@10379: uint32 cur_ticks = GetTime(); rubidium@10379: uint32 last_cur_ticks = cur_ticks; rubidium@10379: uint32 next_tick = cur_ticks + 30; rubidium@10379: uint32 pal_tick = 0; rubidium@10379: rubidium@10379: for (;;) { rubidium@10379: uint32 prev_cur_ticks = cur_ticks; // to check for wrapping rubidium@10379: InteractiveRandom(); // randomness rubidium@10379: rubidium@10379: PollEvent(); rubidium@10379: if (_exit_game) return; rubidium@10379: rubidium@10379: #if defined(_DEBUG) rubidium@10379: if (_shift_pressed) rubidium@10379: #else rubidium@10379: /* Speedup when pressing tab, except when using ALT+TAB rubidium@10379: * to switch to another application */ rubidium@10380: if (key[KEY_TAB] && (key_shifts & KB_ALT_FLAG) == 0) rubidium@10379: #endif rubidium@10379: { rubidium@10379: if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; rubidium@10379: } else if (_fast_forward & 2) { rubidium@10379: _fast_forward = 0; rubidium@10379: } rubidium@10379: rubidium@10379: cur_ticks = GetTime(); rubidium@10379: if (cur_ticks >= next_tick || (_fast_forward && !_pause_game) || cur_ticks < prev_cur_ticks) { rubidium@10379: _realtime_tick += cur_ticks - last_cur_ticks; rubidium@10379: last_cur_ticks = cur_ticks; rubidium@10379: next_tick = cur_ticks + 30; rubidium@10379: rubidium@10379: bool old_ctrl_pressed = _ctrl_pressed; rubidium@10379: rubidium@10379: _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG); rubidium@10379: _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG); rubidium@10379: rubidium@10379: /* determine which directional keys are down */ rubidium@10379: _dirkeys = rubidium@10379: (key[KEY_LEFT] ? 1 : 0) | rubidium@10379: (key[KEY_UP] ? 2 : 0) | rubidium@10379: (key[KEY_RIGHT] ? 4 : 0) | rubidium@10379: (key[KEY_DOWN] ? 8 : 0); rubidium@10379: rubidium@10379: if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); rubidium@10379: rubidium@10379: GameLoop(); rubidium@10379: rubidium@10379: _screen.dst_ptr = _allegro_screen->line[0]; rubidium@10379: UpdateWindows(); rubidium@10379: if (++pal_tick > 4) { rubidium@10379: CheckPaletteAnim(); rubidium@10379: pal_tick = 1; rubidium@10379: } rubidium@10379: DrawSurfaceToScreen(); rubidium@10379: } else { rubidium@10379: CSleep(1); rubidium@10379: _screen.dst_ptr = _allegro_screen->line[0]; rubidium@10379: NetworkDrawChatMessage(); rubidium@10379: DrawMouseCursor(); rubidium@10379: DrawSurfaceToScreen(); rubidium@10379: } rubidium@10379: } rubidium@10379: } rubidium@10379: rubidium@10379: bool VideoDriver_Allegro::ChangeResolution(int w, int h) rubidium@10379: { rubidium@10379: return CreateMainSurface(w, h); rubidium@10379: } rubidium@10379: rubidium@10379: bool VideoDriver_Allegro::ToggleFullscreen(bool fullscreen) rubidium@10379: { rubidium@10379: _fullscreen = fullscreen; rubidium@10379: GetVideoModes(); // get the list of available video modes rubidium@10379: if (_num_resolutions == 0 || !this->ChangeResolution(_cur_resolution.width, _cur_resolution.height)) { rubidium@10379: /* switching resolution failed, put back full_screen to original status */ rubidium@10379: _fullscreen ^= true; rubidium@10379: return false; rubidium@10379: } rubidium@10379: return true; rubidium@10379: } rubidium@10379: rubidium@10379: #endif /* WITH_ALLEGRO */