1 /*
2  * Seven Kingdoms: Ancient Adversaries
3  *
4  * Copyright 1997,1998 Enlight Software Ltd.
5  * Copyright 2010,2015 Jesse Allen
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 //Filename    : OVGA.cpp
23 //Description : VGA management class (SDL version)
24 
25 #include <OVGA.h>
26 #include <OMOUSE.h>
27 #include <OCOLTBL.h>
28 #include <OSYS.h>
29 #include <dbglog.h>
30 #include <version.h>
31 #include <FilePath.h>
32 #include <ConfigAdv.h>
33 
34 DBGLOG_DEFAULT_CHANNEL(Vga);
35 
36 //--------- Declare static functions ---------//
37 
38 static void init_dpi();
39 static int init_window_flags();
40 static void init_window_size();
41 
42 //------ Define static class member vars ---------//
43 
44 char    Vga::use_back_buf = 0;
45 char    Vga::opaque_flag  = 0;
46 VgaBuf* Vga::active_buf   = &vga_front;      // default: front buffer
47 
48 //-------- Begin of function Vga::Vga ----------//
49 
Vga()50 Vga::Vga()
51 {
52    memset(game_pal, 0, sizeof(SDL_Color)*VGA_PALETTE_SIZE);
53    custom_pal = NULL;
54    vga_color_table = NULL;
55 
56    target = NULL;
57    texture = NULL;
58    renderer = NULL;
59    window = NULL;
60 }
61 //-------- End of function Vga::Vga ----------//
62 
63 
64 //-------- Begin of function Vga::~Vga ----------//
65 
~Vga()66 Vga::~Vga()
67 {
68    deinit();
69 }
70 //-------- End of function Vga::~Vga ----------//
71 
72 
73 //-------- Begin of function Vga::init ----------//
74 
init()75 int Vga::init()
76 {
77    SDL_Surface *icon;
78 
79 #ifdef USE_WINDOWS
80    init_dpi();
81 #endif
82 
83    win_grab_forced = 0;
84    win_grab_user_mode = 0;
85    mouse_mode = MOUSE_INPUT_ABS;
86    boundary_set = 0;
87 
88    if (SDL_Init(SDL_INIT_VIDEO))
89       return 0;
90 
91    init_window_size();
92 
93    // Save the mouse position to restore after mode change. If we don't do
94    // this, then the old position gets recalculated, with the mode change
95    // affecting the location, causing a jump.
96    int mouse_x, mouse_y;
97    SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
98 
99    window = SDL_CreateWindow(WIN_TITLE,
100                              SDL_WINDOWPOS_UNDEFINED,
101                              SDL_WINDOWPOS_UNDEFINED,
102                              config_adv.vga_window_width,
103                              config_adv.vga_window_height,
104                              init_window_flags());
105    if( !window )
106       return 0;
107 
108    if( config_adv.vga_full_screen )
109       set_window_grab(WINGRAB_ON);
110 
111    renderer = SDL_CreateRenderer(window, -1, 0);
112    if( !renderer )
113       return 0;
114 
115    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
116    SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
117    if( config_adv.vga_keep_aspect_ratio )
118       SDL_RenderSetLogicalSize(renderer, VGA_WIDTH, VGA_HEIGHT);
119 
120    SDL_WarpMouseGlobal(mouse_x, mouse_y); // warp to initialize mouse by event queue
121 
122    Uint32 window_pixel_format = SDL_GetWindowPixelFormat(window);
123    if (window_pixel_format == SDL_PIXELFORMAT_UNKNOWN)
124    {
125       ERR("Unknown pixel format: %s\n", SDL_GetError());
126       return 0;
127    }
128 
129    // Cannot use SDL_PIXELFORMAT_INDEX8:
130    //   Palettized textures are not supported
131    texture = SDL_CreateTexture(renderer,
132                                window_pixel_format,
133                                SDL_TEXTUREACCESS_STREAMING,
134                                VGA_WIDTH,
135                                VGA_HEIGHT);
136    if (!texture)
137    {
138       ERR("Could not create texture: %s\n", SDL_GetError());
139       return 0;
140    }
141 
142    int desktop_bpp = 0;
143    if (SDL_PIXELTYPE(window_pixel_format) == SDL_PIXELTYPE_PACKED32)
144    {
145       desktop_bpp = 32;
146    }
147    else if (SDL_PIXELTYPE(window_pixel_format) == SDL_PIXELTYPE_PACKED16)
148    {
149       desktop_bpp = 16;
150    }
151    else if (SDL_PIXELTYPE(window_pixel_format) == SDL_PIXELTYPE_PACKED8)
152    {
153       desktop_bpp = 8;
154    }
155    else
156    {
157       ERR("Unsupported pixel type\n");
158       return 0;
159    }
160 
161    target = SDL_CreateRGBSurface(0,
162                                  VGA_WIDTH,
163                                  VGA_HEIGHT,
164                                  desktop_bpp,
165                                  0, 0, 0, 0);
166    if (!target)
167    {
168       return 0;
169    }
170 
171    FilePath icon_path(sys.dir_image);
172    icon_path += "7K_ICON.BMP";
173    icon = SDL_LoadBMP(icon_path);
174    if (icon)
175    {
176       Uint32 colorkey;
177       colorkey = SDL_MapRGB(icon->format, 0, 0, 0);
178       SDL_SetColorKey(icon, SDL_TRUE, colorkey);
179       SDL_SetWindowIcon(window, icon);
180       SDL_FreeSurface(icon);
181    }
182 
183    return 1;
184 }
185 //-------- End of function Vga::init ----------//
186 
187 
188 //-------- Begin of function Vga::deinit ----------//
189 
deinit()190 void Vga::deinit()
191 {
192    SDL_SetRelativeMouseMode(SDL_FALSE);
193    mouse_mode = MOUSE_INPUT_ABS;
194 
195    vga_back.deinit();
196    if (sys.debug_session)
197       vga_true_front.deinit();
198    vga_front.deinit();
199 
200    if (vga_color_table)
201       delete vga_color_table;
202    vga_color_table = NULL;
203    if( custom_pal )
204       mem_del(custom_pal);
205    custom_pal = NULL;
206 
207    if( target )
208       SDL_FreeSurface(target);
209    target = NULL;
210    if( texture )
211       SDL_DestroyTexture(texture);
212    texture = NULL;
213    if( renderer )
214       SDL_DestroyRenderer(renderer);
215    renderer = NULL;
216    if( window )
217       SDL_DestroyWindow(window);
218    window = NULL;
219 
220    SDL_Quit();
221 }
222 //-------- End of function Vga::deinit ----------//
223 
224 
225 //--------- Start of function Vga::load_pal ----------//
226 //
227 // Loads the default game palette specified by fileName. Creates the ddraw
228 // palette.
229 //
load_pal(const char * fileName)230 int Vga::load_pal(const char* fileName)
231 {
232    char palBuf[VGA_PALETTE_SIZE][3];
233    File palFile;
234 
235    palFile.file_open(fileName);
236    palFile.file_seek(8);               // bypass the header info
237    palFile.file_read(palBuf, VGA_PALETTE_SIZE*3);
238    palFile.file_close();
239 
240    for (int i = 0; i < VGA_PALETTE_SIZE; i++)
241    {
242       game_pal[i].r = palBuf[i][0];
243       game_pal[i].g = palBuf[i][1];
244       game_pal[i].b = palBuf[i][2];
245    }
246 
247    //----- initialize interface color table -----//
248 
249    PalDesc palDesc( (unsigned char*) game_pal, sizeof(SDL_Color), VGA_PALETTE_SIZE, 8);
250    vga_color_table = new ColorTable;
251    vga_color_table->generate_table( MAX_BRIGHTNESS_ADJUST_DEGREE, palDesc, ColorTable::bright_func );
252 
253    return 1;
254 }
255 //----------- End of function Vga::load_pal ----------//
256 
257 
258 //-------- Begin of function Vga::activate_pal ----------//
259 //
260 // we are getting the palette focus, select our palette
261 //
activate_pal(VgaBuf * vgaBufPtr)262 void Vga::activate_pal(VgaBuf* vgaBufPtr)
263 {
264    vgaBufPtr->activate_pal(game_pal);
265 }
266 //--------- End of function Vga::activate_pal ----------//
267 
268 
269 //-------- Begin of function Vga::set_custom_palette ----------//
270 //
271 // Read the custom palette specified by fileName and set to display.
272 //
set_custom_palette(char * fileName)273 int Vga::set_custom_palette(char *fileName)
274 {
275    if (!custom_pal)
276       custom_pal = (SDL_Color*)mem_add(sizeof(SDL_Color)*VGA_PALETTE_SIZE);
277 
278    char palBuf[VGA_PALETTE_SIZE][3];
279    File palFile;
280 
281    palFile.file_open(fileName);
282    palFile.file_seek(8);     				// bypass the header info
283    palFile.file_read(palBuf, VGA_PALETTE_SIZE*3);
284    palFile.file_close();
285 
286    for(int i=0; i<VGA_PALETTE_SIZE; i++)
287    {
288       custom_pal[i].r = palBuf[i][0];
289       custom_pal[i].g = palBuf[i][1];
290       custom_pal[i].b = palBuf[i][2];
291    }
292 
293    vga_front.activate_pal(custom_pal);
294 
295    return 1;
296 }
297 //--------- End of function Vga::set_custom_palette ----------//
298 
299 
300 //--------- Begin of function Vga::free_custom_palette ----------//
301 //
302 // Frees the custom palette and restores the game palette.
303 //
free_custom_palette()304 void Vga::free_custom_palette()
305 {
306    if (custom_pal)
307    {
308       mem_del(custom_pal);
309       custom_pal = NULL;
310    }
311    vga_front.activate_pal(game_pal);
312 }
313 //--------- End of function Vga::free_custom_palette ----------//
314 
315 
316 //-------- Begin of function Vga::adjust_brightness ----------//
317 //
318 // <int> changeValue - the value to add to the RGB values of
319 //                     all the colors in the palette.
320 //                     the value can be from -255 to 255.
321 //
322 // <int> preserveContrast - whether preserve the constrast or not
323 //
adjust_brightness(int changeValue)324 void Vga::adjust_brightness(int changeValue)
325 {
326    //---- find out the maximum rgb value can change without affecting the contrast ---//
327 
328    int          i;
329    int          newRed, newGreen, newBlue;
330    SDL_Color palBuf[VGA_PALETTE_SIZE];
331 
332    //------------ change palette now -------------//
333 
334    for( i=0 ; i<VGA_PALETTE_SIZE ; i++ )
335    {
336       newRed   = (int)game_pal[i].r + changeValue;
337       newGreen = (int)game_pal[i].g + changeValue;
338       newBlue  = (int)game_pal[i].b + changeValue;
339 
340       palBuf[i].r = MIN(255, MAX(newRed,0));
341       palBuf[i].g = MIN(255, MAX(newGreen,0));
342       palBuf[i].b = MIN(255, MAX(newBlue,0));
343    }
344 
345    //------------ set palette ------------//
346 
347    vga_front.temp_unlock();
348 
349    vga_front.activate_pal(palBuf);
350 
351    vga_front.temp_restore_lock();
352 }
353 //--------- End of function Vga::adjust_brightness ----------//
354 
355 
356 //-------- Begin of function Vga::handle_messages --------//
handle_messages()357 void Vga::handle_messages()
358 {
359    SDL_Event event;
360 
361    while( SDL_PollEvent(&event) )
362    {
363       switch (event.type)
364       {
365       case SDL_QUIT:
366          sys.signal_exit_flag = 1;
367          break;
368       case SDL_MULTIGESTURE:
369          if (event.mgesture.numFingers == 2) {
370             mouse.process_scroll(event.mgesture.x, event.mgesture.y);
371          }
372          break;
373       case SDL_FINGERDOWN:
374          mouse.end_scroll();
375          break;
376       case SDL_MOUSEWHEEL:
377           mouse.process_scroll(event.wheel.x, event.wheel.y * -1);
378           break;
379       case SDL_WINDOWEVENT:
380          switch (event.window.event)
381          {
382             case SDL_WINDOWEVENT_EXPOSED:
383             case SDL_WINDOWEVENT_RESIZED:
384                sys.need_redraw_flag = 1;
385                update_mouse_pos();
386                boundary_set = 0;
387                break;
388 
389             //case SDL_WINDOWEVENT_ENTER: // Do not respond to mouse focus
390             case SDL_WINDOWEVENT_FOCUS_GAINED:
391             case SDL_WINDOWEVENT_RESTORED:
392                sys.need_redraw_flag = 1;
393                if( !sys.is_mp_game && config_adv.vga_pause_on_focus_loss )
394                   sys.unpause();
395 
396                // update ctrl/shift/alt key state
397                mouse.update_skey_state();
398                SDL_ShowCursor(SDL_DISABLE);
399                break;
400 
401             //case SDL_WINDOWEVENT_LEAVE: // Do not respond to mouse focus
402             case SDL_WINDOWEVENT_FOCUS_LOST:
403             case SDL_WINDOWEVENT_MINIMIZED:
404                if( !sys.is_mp_game && config_adv.vga_pause_on_focus_loss )
405                   sys.pause();
406                // turn the system cursor back on to get around a fullscreen
407                // mouse grabbed problem on windows
408                SDL_ShowCursor(SDL_ENABLE);
409                break;
410          }
411          break;
412 
413       case SDL_MOUSEMOTION:
414          if( mouse_mode == MOUSE_INPUT_ABS )
415          {
416             int logical_x, logical_y;
417             if( config_adv.vga_keep_aspect_ratio )
418             {
419                logical_x = event.motion.x;
420                logical_y = event.motion.y;
421             }
422             else
423             {
424                float xscale, yscale;
425                get_window_scale(&xscale, &yscale);
426                logical_x = ((float)event.motion.x / xscale);
427                logical_y = ((float)event.motion.y / yscale);
428             }
429             if( win_grab_user_mode || win_grab_forced )
430             {
431                int real_x, real_y, do_warp;
432                SDL_GetMouseState(&real_x, &real_y);
433                do_warp = 0;
434                if( !boundary_set )
435                   update_boundary();
436                if( real_x < bound_x1 )
437                {
438                   do_warp = 1;
439                   real_x = bound_x1;
440                   logical_x = mouse.bound_x1;
441                }
442                else if( real_x >= bound_x2 )
443                {
444                   do_warp = 1;
445                   real_x = bound_x2;
446                   logical_x = mouse.bound_x2;
447                }
448                if( real_y < bound_y1 )
449                {
450                   do_warp = 1;
451                   real_y = bound_y1;
452                   logical_y = mouse.bound_y1;
453                }
454                else if( real_y >= bound_y2 )
455                {
456                   do_warp = 1;
457                   real_y = bound_y2;
458                   logical_y = mouse.bound_y2;
459                }
460                if( do_warp )
461                {
462                   SDL_WarpMouseInWindow(window, real_x, real_y);
463                }
464             }
465             mouse.process_mouse_motion(logical_x, logical_y);
466          }
467          else
468          {
469             mouse.process_mouse_motion(event.motion.xrel, event.motion.yrel);
470          }
471          break;
472       case SDL_MOUSEBUTTONDOWN:
473          if( event.button.button == SDL_BUTTON_LEFT )
474          {
475             mouse.add_event(LEFT_BUTTON);
476          }
477          else if( event.button.button == SDL_BUTTON_RIGHT )
478          {
479             mouse.add_event(RIGHT_BUTTON);
480          }
481          set_window_grab(WINGRAB_FORCE);
482          break;
483       case SDL_MOUSEBUTTONUP:
484          if( event.button.button == SDL_BUTTON_LEFT )
485          {
486             mouse.add_event(LEFT_BUTTON_RELEASE);
487             mouse.reset_boundary();
488          }
489          else if( event.button.button == SDL_BUTTON_RIGHT )
490          {
491             mouse.add_event(RIGHT_BUTTON_RELEASE);
492          }
493          set_window_grab(WINGRAB_RESTORE);
494          break;
495       case SDL_KEYDOWN:
496       {
497          int bypass = 0;
498          int mod = event.key.keysym.mod &
499             (KMOD_CTRL|KMOD_SHIFT|KMOD_ALT);
500          if( mod == KMOD_LALT || mod == KMOD_RALT )
501          {
502             if( event.key.keysym.sym == SDLK_RETURN )
503             {
504                bypass = 1;
505                sys.toggle_full_screen_flag = 1;
506             }
507             else if( event.key.keysym.sym == SDLK_F4 )
508             {
509                bypass = 1;
510                sys.signal_exit_flag = 1;
511             }
512             else if( event.key.keysym.sym == SDLK_TAB )
513             {
514                bypass = 1;
515                SDL_Window *window = SDL_GetWindowFromID(event.key.windowID);
516                SDL_MinimizeWindow(window);
517             }
518          }
519          else if( mod == KMOD_LCTRL || mod == KMOD_RCTRL )
520          {
521             if( event.key.keysym.sym == SDLK_g )
522             {
523                bypass = 1;
524                set_window_grab(WINGRAB_TOGGLE);
525             }
526             else if( event.key.keysym.sym == SDLK_m )
527             {
528                bypass = 1;
529                if( mouse_mode == MOUSE_INPUT_ABS && !is_input_grabbed() )
530                   set_mouse_mode( MOUSE_INPUT_REL_WARP );
531                else if( mouse_mode != MOUSE_INPUT_ABS )
532                   set_mouse_mode( MOUSE_INPUT_ABS );
533             }
534          }
535          if( SDL_IsTextInputActive() && event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z )
536 		bypass = 1;
537          if( !bypass )
538          {
539             mouse.update_skey_state();
540             mouse.add_key_event(event.key.keysym.sym, misc.get_time());
541          }
542          break;
543       }
544       case SDL_KEYUP:
545          mouse.update_skey_state();
546          break;
547       case SDL_TEXTINPUT:
548          mouse.add_typing_event(event.text.text, misc.get_time());
549          break;
550       case SDL_TEXTEDITING:
551       case SDL_JOYAXISMOTION:
552       case SDL_JOYBALLMOTION:
553       case SDL_JOYHATMOTION:
554       case SDL_JOYBUTTONDOWN:
555       case SDL_JOYBUTTONUP:
556       default:
557          MSG("unhandled event %d\n", event.type);
558          break;
559       }
560    }
561 }
562 //-------- End of function Vga::handle_messages --------//
563 
564 //-------- Begin of function Vga::flag_redraw --------//
flag_redraw()565 void Vga::flag_redraw()
566 {
567 }
568 //-------- End of function Vga::flag_redraw ----------//
569 
570 
571 //-------- Begin of function Vga::is_full_screen --------//
572 //
is_full_screen()573 int Vga::is_full_screen()
574 {
575    return ((SDL_GetWindowFlags(window) & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0);
576 }
577 //-------- End of function Vga::is_full_screen ----------//
578 
579 
580 //-------- Begin of function Vga::is_input_grabbed --------//
581 //
is_input_grabbed()582 int Vga::is_input_grabbed()
583 {
584    return ((SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_GRABBED) != 0);
585 }
586 //-------- End of function Vga::is_input_grabbed ----------//
587 
588 
589 //-------- Begin of function Vga::update_boundary --------//
590 // Uses logical boundary coordinates and scales them to the actual boundary in
591 // the scaled window.
update_boundary()592 void Vga::update_boundary()
593 {
594    float xscale, yscale;
595    SDL_Rect rect;
596    get_window_scale(&xscale, &yscale);
597    SDL_RenderGetViewport(renderer, &rect);
598    bound_x1 = ((float)(mouse.bound_x1 + rect.x) * xscale);
599    bound_x2 = ((float)(mouse.bound_x2 + rect.x) * xscale);
600    bound_y1 = ((float)(mouse.bound_y1 + rect.y) * yscale);
601    bound_y2 = ((float)(mouse.bound_y2 + rect.y) * yscale);
602    boundary_set = 1;
603 }
604 //-------- End of function Vga::update_boundary --------//
605 
606 
607 //-------- Begin of function Vga::update_mouse_pos ----------//
608 // Updates logical mouse position according to actual mouse state.
update_mouse_pos()609 void Vga::update_mouse_pos()
610 {
611    float xscale, yscale;
612    SDL_Rect rect;
613    int logical_x, logical_y, win_x, win_y, mouse_x, mouse_y;
614 
615    if( !window )
616       return;
617 
618    get_window_scale(&xscale, &yscale);
619    SDL_RenderGetViewport(renderer, &rect);
620    SDL_GetWindowPosition(window, &win_x, &win_y);
621    SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
622    logical_x = ((float)(mouse_x - win_x) / xscale - rect.x);
623    logical_y = ((float)(mouse_y - win_y) / yscale - rect.y);
624 
625    mouse.process_mouse_motion(logical_x, logical_y);
626 }
627 //---------- End of function Vga::update_mouse_pos ----------//
628 
629 
630 //-------- Begin of function Vga::set_full_screen_mode --------//
631 //
632 // mode -1: toggle
633 // mode  0: windowed
634 // mode  1: full screen without display mode change (stretched to desktop)
set_full_screen_mode(int mode)635 void Vga::set_full_screen_mode(int mode)
636 {
637    int result, mouse_x, mouse_y;
638    uint32_t flags = config_adv.vga_full_screen_desktop ?
639       SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
640 
641    switch (mode)
642    {
643       case -1:
644          if( is_full_screen() )
645             flags = 0;
646          break;
647       case 0:
648          flags = 0;
649          break;
650       case 1:
651          break;
652       default:
653          err_now("invalid mode");
654    }
655 
656    // Save the mouse position to restore after mode change. If we don't do
657    // this, then the old position gets recalculated, with the mode change
658    // affecting the location, causing a jump.
659    SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
660 
661    result = SDL_SetWindowFullscreen(window, flags);
662    if (result < 0) {
663       ERR("Could not toggle fullscreen: %s\n", SDL_GetError());
664       return;
665    }
666 
667    sys.need_redraw_flag = 1;
668    boundary_set = 0;
669    if( flags ) // went full screen
670       set_window_grab(WINGRAB_ON);
671    else
672       set_window_grab(WINGRAB_OFF);
673 
674    SDL_WarpMouseGlobal(mouse_x, mouse_y);
675 }
676 //-------- End of function Vga::set_full_screen_mode ----------//
677 
678 
679 //-------- Begin of function Vga::set_mouse_mode --------//
set_mouse_mode(MouseInputMode mode)680 void Vga::set_mouse_mode(MouseInputMode mode)
681 {
682    switch( mode )
683    {
684    case MOUSE_INPUT_REL:
685       if( mouse_mode == MOUSE_INPUT_ABS )
686          SDL_SetRelativeMouseMode(SDL_TRUE);
687       SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0");
688       mouse_mode = MOUSE_INPUT_REL;
689       break;
690    case MOUSE_INPUT_REL_WARP:
691       if( mouse_mode == MOUSE_INPUT_ABS )
692          SDL_SetRelativeMouseMode(SDL_TRUE);
693       SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
694       mouse_mode = MOUSE_INPUT_REL_WARP;
695       break;
696    default:
697       // absolute mode
698       if( mouse_mode != MOUSE_INPUT_ABS )
699          SDL_SetRelativeMouseMode(SDL_FALSE);
700       mouse_mode = MOUSE_INPUT_ABS;
701    }
702 }
703 //-------- End of function Vga::set_mouse_mode --------//
704 
705 
706 //-------- Begin of function Vga::set_window_grab --------//
707 //
708 // WINGRAB_OFF = Turn window grab off, except when forced on
709 // WINGRAB_ON = Turn window grab on
710 // WINGRAB_TOGGLE = Toggle window grab, used for the grab key
711 // WINGRAB_FORCE = Force grab on, even if user has it off
712 // WINGRAB_RESTORE = Disable forcing, and user's option
set_window_grab(WinGrab mode)713 void Vga::set_window_grab(WinGrab mode)
714 {
715    switch( mode )
716    {
717    case WINGRAB_OFF:
718       if( win_grab_user_mode )
719       {
720          win_grab_user_mode = 0;
721          if( !win_grab_forced )
722          {
723             SDL_SetWindowGrab(window, SDL_FALSE);
724             if( mouse_mode != MOUSE_INPUT_ABS )
725                set_mouse_mode(MOUSE_INPUT_ABS);
726          }
727       }
728       break;
729    case WINGRAB_ON:
730       if( !win_grab_user_mode )
731       {
732          win_grab_user_mode = 1;
733          if( !win_grab_forced )
734          {
735             SDL_SetWindowGrab(window, SDL_TRUE);
736          }
737       }
738       break;
739    case WINGRAB_TOGGLE:
740       if( win_grab_user_mode )
741       {
742          win_grab_user_mode = 0;
743          if( !win_grab_forced )
744          {
745             SDL_SetWindowGrab(window, SDL_FALSE);
746             if( mouse_mode != MOUSE_INPUT_ABS )
747                set_mouse_mode(MOUSE_INPUT_ABS);
748          }
749       }
750       else
751       {
752          win_grab_user_mode = 1;
753          if( !win_grab_forced )
754          {
755             SDL_SetWindowGrab(window, SDL_TRUE);
756          }
757       }
758       break;
759    case WINGRAB_FORCE:
760       if( !win_grab_forced )
761       {
762          win_grab_forced = 1;
763          if( !win_grab_user_mode )
764          {
765             SDL_SetWindowGrab(window, SDL_TRUE);
766          }
767       }
768       break;
769    case WINGRAB_RESTORE:
770       if( win_grab_forced )
771       {
772          win_grab_forced = 0;
773          if( !win_grab_user_mode )
774          {
775             SDL_SetWindowGrab(window, SDL_FALSE);
776             if( mouse_mode != MOUSE_INPUT_ABS )
777                set_mouse_mode(MOUSE_INPUT_ABS);
778          }
779       }
780       break;
781    default:
782       err_now("invalid mode");
783    }
784 }
785 //-------- End of function Vga::set_window_grab ----------//
786 
787 
788 //-------- Beginning of function Vga::flip ----------//
flip()789 void Vga::flip()
790 {
791    static Uint32 ticks = 0;
792    Uint32 cur_ticks;
793 
794    if( !is_inited() )
795       return;
796 
797    cur_ticks = SDL_GetTicks();
798    if (cur_ticks > ticks + 17 || cur_ticks < ticks) {
799       ticks = cur_ticks;
800       SDL_BlitSurface(vga_front.surface, NULL, target, NULL);
801       SDL_UpdateTexture(texture, NULL, target->pixels, target->pitch);
802       SDL_RenderClear(renderer);
803       SDL_RenderCopy(renderer, texture, NULL, NULL);
804       SDL_RenderPresent(renderer);
805    }
806 }
807 //-------- End of function Vga::flip ----------//
808 
809 
810 //-------- Beginning of function Vga::save_status_report ----------//
save_status_report()811 void Vga::save_status_report()
812 {
813    FilePath path(sys.dir_config);
814    FILE *file;
815    int num, i;
816    const char *s;
817    SDL_version ver;
818 
819    path += "sdl.txt";
820    if( path.error_flag )
821       return;
822 
823    file = fopen(path, "w");
824    if( !file )
825       return;
826 
827    fprintf(file, "=== Seven Kingdoms " SKVERSION " ===\n");
828    s = SDL_GetPlatform();
829    fprintf(file, "Platform: %s\n", s);
830    if( SDL_BYTEORDER == SDL_BIG_ENDIAN )
831       fprintf(file, "Big endian\n");
832    else
833       fprintf(file, "Little endian\n");
834 #ifndef HAVE_KNOWN_BUILD
835    fprintf(file, "Binary built using unsupported configuration\n");
836 #endif
837 
838    s = SDL_GetCurrentVideoDriver();
839    fprintf(file, "Current SDL video driver: %s\n", s);
840    SDL_GetVersion(&ver);
841    fprintf(file, "SDL version: %d.%d.%d\n", ver.major, ver.minor, ver.patch);
842    SDL_VERSION(&ver);
843    fprintf(file, "Compiled SDL version: %d.%d.%d\n\n", ver.major, ver.minor, ver.patch);
844 
845    fprintf(file, "-- Video drivers --\n");
846    num = SDL_GetNumVideoDrivers();
847    for( i=0; i<num; i++ )
848    {
849       s = SDL_GetVideoDriver(i);
850       fprintf(file, "%d: %s\n", i, s);
851    }
852    fprintf(file, "\n");
853 
854    if( window )
855    {
856       int x, y, w, h = 0;
857       SDL_RendererInfo info;
858       SDL_Renderer *r = SDL_GetRenderer(window);
859 
860       fprintf(file, "-- Current window --\n");
861       fprintf(file, "Active on display: %d\n", SDL_GetWindowDisplayIndex(window));
862       SDL_GetWindowPosition(window, &x, &y);
863       SDL_GetWindowSize(window, &w, &h);
864       fprintf(file, "Geometry: %dx%d @ (%d, %d)\n", w, h, x, y);
865       fprintf(file, "Pixel format: %s\n", SDL_GetPixelFormatName(SDL_GetWindowPixelFormat(window)));
866       fprintf(file, "Full screen: %s\n", is_full_screen() ? "yes" : "no");
867       fprintf(file, "Input grabbed: %s\n\n", SDL_GetWindowGrab(window) ? "yes" : "no");
868       if( r )
869       {
870          SDL_RendererInfo info;
871          float xscale, yscale;
872          SDL_Rect rect;
873 
874          SDL_GetRendererInfo(r, &info);
875          get_window_scale(&xscale, &yscale);
876          SDL_RenderGetViewport(renderer, &rect);
877          SDL_RenderGetLogicalSize(renderer, &w, &h);
878 
879          fprintf(file, "-- Current renderer: %s --\n", info.name);
880          fprintf(file, "Viewport: x=%d,y=%d,w=%d,h=%d\n", rect.x, rect.y, rect.w, rect.h);
881          fprintf(file, "Scale: xscale=%f,yscale=%f\n", xscale, yscale);
882          fprintf(file, "Logical size: w=%d, h=%d\n", w, h);
883          fprintf(file, "Capabilities: %s\n", info.flags & SDL_RENDERER_ACCELERATED ? "hardware accelerated" : "software fallback");
884          fprintf(file, "V-sync: %s\n", info.flags & SDL_RENDERER_PRESENTVSYNC ? "on" : "off");
885          fprintf(file, "Rendering to texture supported: %s\n", info.flags & SDL_RENDERER_TARGETTEXTURE ? "yes" : "no");
886          if( info.max_texture_width || info.max_texture_height )
887             fprintf(file, "Maximum texture size: %dx%d\n", info.max_texture_width, info.max_texture_height);
888          fprintf(file, "Pixel formats:\n");
889          for( unsigned i=0; i<info.num_texture_formats; i++ )
890             fprintf(file, "\t%s\n", SDL_GetPixelFormatName(info.texture_formats[i]));
891       }
892    }
893    fprintf(file, "\n");
894 
895    if( texture )
896    {
897       uint32_t format = 0;
898       int w, h = 0;
899       SDL_QueryTexture(texture, &format, NULL, &w, &h);
900       fprintf(file, "-- Streaming texture --\n");
901       fprintf(file, "Size: %dx%d\n", w, h);
902       fprintf(file, "Pixel format: %s\n\n", SDL_GetPixelFormatName(format));
903    }
904 
905    num = SDL_GetNumVideoDisplays();
906    for( i=0; i<num; i++ )
907    {
908       SDL_Rect rect;
909       SDL_DisplayMode mode;
910       float ddpi, hdpi, vdpi;
911       fprintf(file, "-- Display %d --\n", i);
912       if( !SDL_GetCurrentDisplayMode(i, &mode) )
913          fprintf(file, "Mode: %dx%dx%ubpp %dHz format=%s driver=%p\n", mode.w, mode.h, SDL_BITSPERPIXEL(mode.format), mode.refresh_rate, SDL_GetPixelFormatName(mode.format), mode.driverdata);
914       if( !SDL_GetDisplayDPI(i, &ddpi, &hdpi, &vdpi) )
915          fprintf(file, "DPI: diag=%f horiz=%f vert=%f\n", ddpi, hdpi, vdpi);
916       if( !SDL_GetDisplayBounds(i, &rect) )
917          fprintf(file, "Bounds: x=%d y=%d w=%d h=%d\n", rect.x, rect.y, rect.w, rect.h);
918 #if SDL_VERSION_ATLEAST(2, 0, 5)
919       if( !SDL_GetDisplayUsableBounds(i, &rect) ) // Note: requires SDL 2.0.5+
920          fprintf(file, "Usable bounds: x=%d y=%d w=%d h=%d\n", rect.x, rect.y, rect.w, rect.h);
921 #endif
922       fprintf(file, "\n");
923    }
924 
925    num = SDL_GetNumRenderDrivers();
926    for( i=0; i<num; i++ )
927    {
928       SDL_RendererInfo info;
929       SDL_GetRenderDriverInfo(i, &info);
930       fprintf(file, "-- Renderer %s (%d) --\n", info.name, i);
931       fprintf(file, "Capabilities: %s\n", info.flags & SDL_RENDERER_ACCELERATED ? "hardware accelerated" : "software fallback");
932       fprintf(file, "V-sync capable: %s\n", info.flags & SDL_RENDERER_PRESENTVSYNC ? "on" : "off");
933       fprintf(file, "Rendering to texture supported: %s\n", info.flags & SDL_RENDERER_TARGETTEXTURE ? "yes" : "no");
934       if( info.max_texture_width || info.max_texture_height )
935          fprintf(file, "Maximum texture size: %dx%d\n", info.max_texture_width, info.max_texture_height);
936       fprintf(file, "Pixel formats:\n");
937       for( unsigned j=0; j<info.num_texture_formats; j++ )
938          fprintf(file, "\t%s\n", SDL_GetPixelFormatName(info.texture_formats[j]));
939       fprintf(file, "\n");
940    }
941 
942    fclose(file);
943    return;
944 }
945 //-------- End of function Vga::save_status_report ----------//
946 
947 
948 //-------- Beginning of function Vga::get_window_scale ----------//
get_window_scale(float * xscale,float * yscale)949 void Vga::get_window_scale(float *xscale, float *yscale)
950 {
951    if( config_adv.vga_keep_aspect_ratio )
952    {
953       SDL_RenderGetScale(renderer, xscale, yscale);
954    }
955    else
956    {
957       int w, h;
958       SDL_GetWindowSize(window, &w, &h);
959       *xscale = (float)w / (float)VGA_WIDTH;
960       *yscale = (float)h / (float)VGA_HEIGHT;
961    }
962 }
963 //-------- End of function Vga::get_window_scale ----------//
964 
965 
966 #ifdef USE_WINDOWS
967 
968 #include <windows.h>
969 
970 typedef enum PROCESS_DPI_AWARENESS {
971    PROCESS_DPI_UNAWARE = 0,
972    PROCESS_SYSTEM_DPI_AWARE = 1,
973    PROCESS_PER_MONITOR_DPI_AWARE = 2
974 } PROCESS_DPI_AWARENESS;
975 
976 BOOL(WINAPI *pSetProcessDPIAware)(void); // Vista and later
977 HRESULT(WINAPI *pSetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness); // Windows 8.1 and later
978 
979 // Based on the example provided by Eric Wasylishen
980 // https://discourse.libsdl.org/t/sdl-getdesktopdisplaymode-resolution-reported-in-windows-10-when-using-app-scaling/22389
init_dpi()981 static void init_dpi()
982 {
983    void* userDLL;
984    void* shcoreDLL;
985 
986    shcoreDLL = SDL_LoadObject("SHCORE.DLL");
987    if (shcoreDLL)
988    {
989       pSetProcessDpiAwareness = (HRESULT(WINAPI *)(PROCESS_DPI_AWARENESS)) SDL_LoadFunction(shcoreDLL, "SetProcessDpiAwareness");
990    }
991 
992    if (pSetProcessDpiAwareness)
993    {
994       /* Try Windows 8.1+ version */
995       HRESULT result = pSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
996       return;
997    }
998 
999    userDLL = SDL_LoadObject("USER32.DLL");
1000    if (userDLL)
1001    {
1002       pSetProcessDPIAware = (BOOL(WINAPI *)(void)) SDL_LoadFunction(userDLL, "SetProcessDPIAware");
1003    }
1004 
1005    if (pSetProcessDPIAware)
1006    {
1007       /* Try Vista - Windows 8 version.
1008       This has a constant scale factor for all monitors.
1009       */
1010       BOOL success = pSetProcessDPIAware();
1011    }
1012 }
1013 
1014 #endif
1015 
init_window_flags()1016 static int init_window_flags()
1017 {
1018    int flags = 0;
1019    if( config_adv.vga_full_screen )
1020       flags |= config_adv.vga_full_screen_desktop ?
1021          SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
1022    if( config_adv.vga_allow_highdpi )
1023       flags |= SDL_WINDOW_ALLOW_HIGHDPI;
1024    return flags;
1025 }
1026 
init_window_size()1027 static void init_window_size()
1028 {
1029    if( !config_adv.vga_full_screen_desktop )
1030    {
1031       // must match game's native resolution
1032       config_adv.vga_window_width = 800;
1033       config_adv.vga_window_height = 600;
1034       return;
1035    }
1036 
1037    if( config_adv.vga_window_width && config_adv.vga_window_height )
1038       return;
1039 
1040 #if SDL_VERSION_ATLEAST(2, 0, 5)
1041    int display_idx;
1042    SDL_Window *size_win = SDL_CreateWindow(WIN_TITLE, 0, 0, 1, 1, 0);
1043    if( !size_win )
1044       goto unknown_display;
1045 
1046    display_idx = SDL_GetWindowDisplayIndex(size_win);
1047    SDL_DestroyWindow(size_win);
1048    if( display_idx < 0 )
1049       goto unknown_display;
1050 
1051    SDL_Rect rect;
1052    if( SDL_GetDisplayUsableBounds(display_idx, &rect)<0 )
1053       goto unknown_display;
1054 
1055    if( rect.w >= 1024 && rect.h >= 768 )
1056    {
1057       config_adv.vga_window_width = 1024;
1058       config_adv.vga_window_height = 768;
1059       return;
1060    }
1061 
1062    if( rect.w >= 800 && rect.h >= 600 )
1063    {
1064       config_adv.vga_window_width = 800;
1065       config_adv.vga_window_height = 600;
1066       return;
1067    }
1068 
1069    config_adv.vga_window_width = 640;
1070    config_adv.vga_window_height = 480;
1071    return;
1072 #endif
1073 
1074 unknown_display:
1075       config_adv.vga_window_width = 800;
1076       config_adv.vga_window_height = 600;
1077       return;
1078 }
1079