Add: INR currency (#8136)
[openttd-github.git] / src / video / allegro_v.cpp
blob88e5c528ff0f9c795c9a1fbb6e91cf01e62bb398
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /**
9 * @file allegro_v.cpp Implementation of the Allegro video driver.
10 * @note Implementing threaded pushing of data to the display is
11 * not faster (it's a few percent slower) in contrast to the
12 * results gained with threading it for SDL.
15 #ifdef WITH_ALLEGRO
17 #include "../stdafx.h"
18 #include "../openttd.h"
19 #include "../gfx_func.h"
20 #include "../rev.h"
21 #include "../blitter/factory.hpp"
22 #include "../network/network.h"
23 #include "../core/random_func.hpp"
24 #include "../core/math_func.hpp"
25 #include "../framerate_type.h"
26 #include "../thread.h"
27 #include "allegro_v.h"
28 #include <allegro.h>
29 #include <algorithm>
31 #include "../safeguards.h"
33 #ifdef _DEBUG
34 /* Allegro replaces SEGV/ABRT signals meaning that the debugger will never
35 * be triggered, so rereplace the signals and make the debugger useful. */
36 #include <signal.h>
37 #endif
39 static FVideoDriver_Allegro iFVideoDriver_Allegro;
41 static BITMAP *_allegro_screen;
43 #define MAX_DIRTY_RECTS 100
44 static PointDimension _dirty_rects[MAX_DIRTY_RECTS];
45 static int _num_dirty_rects;
47 void VideoDriver_Allegro::MakeDirty(int left, int top, int width, int height)
49 if (_num_dirty_rects < MAX_DIRTY_RECTS) {
50 _dirty_rects[_num_dirty_rects].x = left;
51 _dirty_rects[_num_dirty_rects].y = top;
52 _dirty_rects[_num_dirty_rects].width = width;
53 _dirty_rects[_num_dirty_rects].height = height;
55 _num_dirty_rects++;
58 static void DrawSurfaceToScreen()
60 PerformanceMeasurer framerate(PFE_VIDEO);
62 int n = _num_dirty_rects;
63 if (n == 0) return;
65 _num_dirty_rects = 0;
66 if (n > MAX_DIRTY_RECTS) {
67 blit(_allegro_screen, screen, 0, 0, 0, 0, _allegro_screen->w, _allegro_screen->h);
68 return;
71 for (int i = 0; i < n; i++) {
72 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);
77 static void UpdatePalette(uint start, uint count)
79 static PALETTE pal;
81 uint end = start + count;
82 for (uint i = start; i != end; i++) {
83 pal[i].r = _cur_palette.palette[i].r / 4;
84 pal[i].g = _cur_palette.palette[i].g / 4;
85 pal[i].b = _cur_palette.palette[i].b / 4;
86 pal[i].filler = 0;
89 set_palette_range(pal, start, end - 1, 1);
92 static void InitPalette()
94 UpdatePalette(0, 256);
97 static void CheckPaletteAnim()
99 if (_cur_palette.count_dirty != 0) {
100 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
102 switch (blitter->UsePaletteAnimation()) {
103 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
104 UpdatePalette(_cur_palette.first_dirty, _cur_palette.count_dirty);
105 break;
107 case Blitter::PALETTE_ANIMATION_BLITTER:
108 blitter->PaletteAnimate(_cur_palette);
109 break;
111 case Blitter::PALETTE_ANIMATION_NONE:
112 break;
114 default:
115 NOT_REACHED();
117 _cur_palette.count_dirty = 0;
121 static const Dimension default_resolutions[] = {
122 { 640, 480},
123 { 800, 600},
124 {1024, 768},
125 {1152, 864},
126 {1280, 800},
127 {1280, 960},
128 {1280, 1024},
129 {1400, 1050},
130 {1600, 1200},
131 {1680, 1050},
132 {1920, 1200}
135 static void GetVideoModes()
137 /* Need to set a gfx_mode as there is NO other way to autodetect for
138 * cards ourselves... and we need a card to get the modes. */
139 set_gfx_mode(_fullscreen ? GFX_AUTODETECT_FULLSCREEN : GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
141 _resolutions.clear();
143 GFX_MODE_LIST *mode_list = get_gfx_mode_list(gfx_driver->id);
144 if (mode_list == nullptr) {
145 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
146 return;
149 GFX_MODE *modes = mode_list->mode;
151 for (int i = 0; modes[i].bpp != 0; i++) {
152 uint w = modes[i].width;
153 uint h = modes[i].height;
154 if (w < 640 || h < 480) continue;
155 if (std::find(_resolutions.begin(), _resolutions.end(), Dimension(w, h)) != _resolutions.end()) continue;
156 _resolutions.emplace_back(w, h);
159 SortResolutions();
161 destroy_gfx_mode_list(mode_list);
164 static void GetAvailableVideoMode(uint *w, uint *h)
166 /* No video modes, so just try it and see where it ends */
167 if (_resolutions.empty()) return;
169 /* is the wanted mode among the available modes? */
170 if (std::find(_resolutions.begin(), _resolutions.end(), Dimension(*w, *h)) != _resolutions.end()) return;
172 /* use the closest possible resolution */
173 uint best = 0;
174 uint delta = Delta(_resolutions[0].width, *w) * Delta(_resolutions[0].height, *h);
175 for (uint i = 1; i != _resolutions.size(); ++i) {
176 uint newdelta = Delta(_resolutions[i].width, *w) * Delta(_resolutions[i].height, *h);
177 if (newdelta < delta) {
178 best = i;
179 delta = newdelta;
182 *w = _resolutions[best].width;
183 *h = _resolutions[best].height;
186 static bool CreateMainSurface(uint w, uint h)
188 int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
189 if (bpp == 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
190 set_color_depth(bpp);
192 GetAvailableVideoMode(&w, &h);
193 if (set_gfx_mode(_fullscreen ? GFX_AUTODETECT_FULLSCREEN : GFX_AUTODETECT_WINDOWED, w, h, 0, 0) != 0) {
194 DEBUG(driver, 0, "Allegro: Couldn't allocate a window to draw on '%s'", allegro_error);
195 return false;
198 /* The size of the screen might be bigger than the part we can actually draw on!
199 * So calculate the size based on the top, bottom, left and right */
200 _allegro_screen = create_bitmap_ex(bpp, screen->cr - screen->cl, screen->cb - screen->ct);
201 _screen.width = _allegro_screen->w;
202 _screen.height = _allegro_screen->h;
203 _screen.pitch = ((byte*)screen->line[1] - (byte*)screen->line[0]) / (bpp / 8);
204 _screen.dst_ptr = _allegro_screen->line[0];
206 /* Initialise the screen so we don't blit garbage to the screen */
207 memset(_screen.dst_ptr, 0, _screen.height * _screen.pitch);
209 /* Set the mouse at the place where we expect it */
210 poll_mouse();
211 _cursor.pos.x = mouse_x;
212 _cursor.pos.y = mouse_y;
214 BlitterFactory::GetCurrentBlitter()->PostResize();
216 InitPalette();
218 char caption[32];
219 seprintf(caption, lastof(caption), "OpenTTD %s", _openttd_revision);
220 set_window_title(caption);
222 enable_hardware_cursor();
223 select_mouse_cursor(MOUSE_CURSOR_ARROW);
224 show_mouse(_allegro_screen);
226 GameSizeChanged();
228 return true;
231 bool VideoDriver_Allegro::ClaimMousePointer()
233 select_mouse_cursor(MOUSE_CURSOR_NONE);
234 show_mouse(nullptr);
235 disable_hardware_cursor();
236 return true;
239 struct VkMapping {
240 uint16 vk_from;
241 byte vk_count;
242 byte map_to;
245 #define AS(x, z) {x, 0, z}
246 #define AM(x, y, z, w) {x, y - x, z}
248 static const VkMapping _vk_mapping[] = {
249 /* Pageup stuff + up/down */
250 AM(KEY_PGUP, KEY_PGDN, WKC_PAGEUP, WKC_PAGEDOWN),
251 AS(KEY_UP, WKC_UP),
252 AS(KEY_DOWN, WKC_DOWN),
253 AS(KEY_LEFT, WKC_LEFT),
254 AS(KEY_RIGHT, WKC_RIGHT),
256 AS(KEY_HOME, WKC_HOME),
257 AS(KEY_END, WKC_END),
259 AS(KEY_INSERT, WKC_INSERT),
260 AS(KEY_DEL, WKC_DELETE),
262 /* Map letters & digits */
263 AM(KEY_A, KEY_Z, 'A', 'Z'),
264 AM(KEY_0, KEY_9, '0', '9'),
266 AS(KEY_ESC, WKC_ESC),
267 AS(KEY_PAUSE, WKC_PAUSE),
268 AS(KEY_BACKSPACE, WKC_BACKSPACE),
270 AS(KEY_SPACE, WKC_SPACE),
271 AS(KEY_ENTER, WKC_RETURN),
272 AS(KEY_TAB, WKC_TAB),
274 /* Function keys */
275 AM(KEY_F1, KEY_F12, WKC_F1, WKC_F12),
277 /* Numeric part. */
278 AM(KEY_0_PAD, KEY_9_PAD, '0', '9'),
279 AS(KEY_SLASH_PAD, WKC_NUM_DIV),
280 AS(KEY_ASTERISK, WKC_NUM_MUL),
281 AS(KEY_MINUS_PAD, WKC_NUM_MINUS),
282 AS(KEY_PLUS_PAD, WKC_NUM_PLUS),
283 AS(KEY_ENTER_PAD, WKC_NUM_ENTER),
284 AS(KEY_DEL_PAD, WKC_DELETE),
286 /* Other non-letter keys */
287 AS(KEY_SLASH, WKC_SLASH),
288 AS(KEY_SEMICOLON, WKC_SEMICOLON),
289 AS(KEY_EQUALS, WKC_EQUALS),
290 AS(KEY_OPENBRACE, WKC_L_BRACKET),
291 AS(KEY_BACKSLASH, WKC_BACKSLASH),
292 AS(KEY_CLOSEBRACE, WKC_R_BRACKET),
294 AS(KEY_QUOTE, WKC_SINGLEQUOTE),
295 AS(KEY_COMMA, WKC_COMMA),
296 AS(KEY_MINUS, WKC_MINUS),
297 AS(KEY_STOP, WKC_PERIOD),
298 AS(KEY_TILDE, WKC_BACKQUOTE),
301 static uint32 ConvertAllegroKeyIntoMy(WChar *character)
303 int scancode;
304 int unicode = ureadkey(&scancode);
306 const VkMapping *map;
307 uint key = 0;
309 for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
310 if ((uint)(scancode - map->vk_from) <= map->vk_count) {
311 key = scancode - map->vk_from + map->map_to;
312 break;
316 if (key_shifts & KB_SHIFT_FLAG) key |= WKC_SHIFT;
317 if (key_shifts & KB_CTRL_FLAG) key |= WKC_CTRL;
318 if (key_shifts & KB_ALT_FLAG) key |= WKC_ALT;
319 #if 0
320 DEBUG(driver, 0, "Scancode character pressed %u", scancode);
321 DEBUG(driver, 0, "Unicode character pressed %u", unicode);
322 #endif
324 *character = unicode;
325 return key;
328 static const uint LEFT_BUTTON = 0;
329 static const uint RIGHT_BUTTON = 1;
331 static void PollEvent()
333 poll_mouse();
335 bool mouse_action = false;
337 /* Mouse buttons */
338 static int prev_button_state;
339 if (prev_button_state != mouse_b) {
340 uint diff = prev_button_state ^ mouse_b;
341 while (diff != 0) {
342 uint button = FindFirstBit(diff);
343 ClrBit(diff, button);
344 if (HasBit(mouse_b, button)) {
345 /* Pressed mouse button */
346 if (_rightclick_emulate && (key_shifts & KB_CTRL_FLAG)) {
347 button = RIGHT_BUTTON;
348 ClrBit(diff, RIGHT_BUTTON);
350 switch (button) {
351 case LEFT_BUTTON:
352 _left_button_down = true;
353 break;
355 case RIGHT_BUTTON:
356 _right_button_down = true;
357 _right_button_clicked = true;
358 break;
360 default:
361 /* ignore rest */
362 break;
364 } else {
365 /* Released mouse button */
366 if (_rightclick_emulate) {
367 _right_button_down = false;
368 _left_button_down = false;
369 _left_button_clicked = false;
370 } else if (button == LEFT_BUTTON) {
371 _left_button_down = false;
372 _left_button_clicked = false;
373 } else if (button == RIGHT_BUTTON) {
374 _right_button_down = false;
378 prev_button_state = mouse_b;
379 mouse_action = true;
382 /* Mouse movement */
383 if (_cursor.UpdateCursorPosition(mouse_x, mouse_y, false)) {
384 position_mouse(_cursor.pos.x, _cursor.pos.y);
386 if (_cursor.delta.x != 0 || _cursor.delta.y) mouse_action = true;
388 static int prev_mouse_z = 0;
389 if (prev_mouse_z != mouse_z) {
390 _cursor.wheel = (prev_mouse_z - mouse_z) < 0 ? -1 : 1;
391 prev_mouse_z = mouse_z;
392 mouse_action = true;
395 if (mouse_action) HandleMouseEvents();
397 poll_keyboard();
398 if ((key_shifts & KB_ALT_FLAG) && (key[KEY_ENTER] || key[KEY_F])) {
399 ToggleFullScreen(!_fullscreen);
400 } else if (keypressed()) {
401 WChar character;
402 uint keycode = ConvertAllegroKeyIntoMy(&character);
403 HandleKeypress(keycode, character);
408 * There are multiple modules that might be using Allegro and
409 * Allegro can only be initiated once.
411 int _allegro_instance_count = 0;
413 const char *VideoDriver_Allegro::Start(const char * const *parm)
415 if (_allegro_instance_count == 0 && install_allegro(SYSTEM_AUTODETECT, &errno, nullptr)) {
416 DEBUG(driver, 0, "allegro: install_allegro failed '%s'", allegro_error);
417 return "Failed to set up Allegro";
419 _allegro_instance_count++;
421 install_timer();
422 install_mouse();
423 install_keyboard();
425 #if defined _DEBUG
426 /* Allegro replaces SEGV/ABRT signals meaning that the debugger will never
427 * be triggered, so rereplace the signals and make the debugger useful. */
428 signal(SIGABRT, nullptr);
429 signal(SIGSEGV, nullptr);
430 #endif
432 GetVideoModes();
433 if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height)) {
434 return "Failed to set up Allegro video";
436 MarkWholeScreenDirty();
437 set_close_button_callback(HandleExitGameRequest);
439 return nullptr;
442 void VideoDriver_Allegro::Stop()
444 if (--_allegro_instance_count == 0) allegro_exit();
447 #if defined(UNIX) || defined(__OS2__)
448 # include <sys/time.h> /* gettimeofday */
450 static uint32 GetTime()
452 struct timeval tim;
454 gettimeofday(&tim, nullptr);
455 return tim.tv_usec / 1000 + tim.tv_sec * 1000;
457 #else
458 static uint32 GetTime()
460 return GetTickCount();
462 #endif
465 void VideoDriver_Allegro::MainLoop()
467 uint32 cur_ticks = GetTime();
468 uint32 last_cur_ticks = cur_ticks;
469 uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
471 CheckPaletteAnim();
473 for (;;) {
474 uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
475 InteractiveRandom(); // randomness
477 PollEvent();
478 if (_exit_game) return;
480 #if defined(_DEBUG)
481 if (_shift_pressed)
482 #else
483 /* Speedup when pressing tab, except when using ALT+TAB
484 * to switch to another application */
485 if (key[KEY_TAB] && (key_shifts & KB_ALT_FLAG) == 0)
486 #endif
488 if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
489 } else if (_fast_forward & 2) {
490 _fast_forward = 0;
493 cur_ticks = GetTime();
494 if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
495 _realtime_tick += cur_ticks - last_cur_ticks;
496 last_cur_ticks = cur_ticks;
497 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
499 bool old_ctrl_pressed = _ctrl_pressed;
501 _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG);
502 _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG);
504 /* determine which directional keys are down */
505 _dirkeys =
506 (key[KEY_LEFT] ? 1 : 0) |
507 (key[KEY_UP] ? 2 : 0) |
508 (key[KEY_RIGHT] ? 4 : 0) |
509 (key[KEY_DOWN] ? 8 : 0);
511 if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
513 GameLoop();
515 UpdateWindows();
516 CheckPaletteAnim();
517 DrawSurfaceToScreen();
518 } else {
519 CSleep(1);
520 NetworkDrawChatMessage();
521 DrawMouseCursor();
522 DrawSurfaceToScreen();
527 bool VideoDriver_Allegro::ChangeResolution(int w, int h)
529 return CreateMainSurface(w, h);
532 bool VideoDriver_Allegro::ToggleFullscreen(bool fullscreen)
534 _fullscreen = fullscreen;
535 GetVideoModes(); // get the list of available video modes
536 if (_resolutions.empty() || !this->ChangeResolution(_cur_resolution.width, _cur_resolution.height)) {
537 /* switching resolution failed, put back full_screen to original status */
538 _fullscreen ^= true;
539 return false;
541 return true;
544 bool VideoDriver_Allegro::AfterBlitterChange()
546 return CreateMainSurface(_screen.width, _screen.height);
549 #endif /* WITH_ALLEGRO */