Purple Martians
Technical Code Descriptions

Display
Variables Creating the display Process display change Toggle fullscreen mode Saving window size and position Display transform double Cycle display transform double
Variables These are the variables for the display:
class mwDisplay
{
   public:

   ALLEGRO_DISPLAY *display = NULL;

   int no_display = 0;

   int desktop_width;
   int desktop_height;

   int disp_x_curr; // either wind in windowed mode or full fullscreen mode)
   int disp_y_curr;
   int disp_w_curr;
   int disp_h_curr;

   int disp_x_wind; // windowed
   int disp_y_wind;
   int disp_w_wind;
   int disp_h_wind;

   int disp_x_full; // fullscreen  (set to 0, 0, desktop_width, desktop_height and never change)
   int disp_y_full;
   int disp_w_full;
   int disp_h_full;

   float SCREEN_W;
   float SCREEN_H;

   int fullscreen = 1;
   int display_adapter_num = 0;

   int show_scale_factor;
   int scale_factor_holdoff;

   int display_transform_double;
   int display_transform_double_max;
   int saved_display_transform_double;
   int show_dtd;
These values are also saved in the config file with these defaults:
mDisplay.disp_x_wind=200
mDisplay.disp_y_wind=40
mDisplay.disp_w_wind=1280
mDisplay.disp_h_wind=1024
mDisplay.scale_factor=1.0
mDisplay.fullscreen=1
mDisplay.display_adapter_num=0
mDisplay.saved_display_transform_double=0
mDisplay.display_transform_double_max=3
Creating the display
int mwDisplay::init_display(void)
{
   if (mMain.headless_server)
   {
      no_display = 1;
      return 0;
   }

   int num_adapters = al_get_num_video_adapters();
   if (num_adapters == 0)
   {
      printf("No Display Adapters Found!\n");
      no_display = 1;
      return 0;
   }

   if (display_adapter_num >=  num_adapters) display_adapter_num = 0;
   al_set_new_display_adapter(display_adapter_num);

   if (num_adapters > 1) printf("%d adapters found...using:%d\n", num_adapters, display_adapter_num);

   ALLEGRO_MONITOR_INFO aminfo;
   al_get_monitor_info(display_adapter_num, &aminfo);
   desktop_width  = aminfo.x2 - aminfo.x1;
   desktop_height = aminfo.y2 - aminfo.y1;
   printf("Desktop Resolution: %dx%d\n", desktop_width, desktop_height);

   mDisplay.disp_x_full = 0; // fullscreen  (set to 0, 0, desktop_width, desktop_height and never change)
   mDisplay.disp_y_full = 0;
   mDisplay.disp_w_full = desktop_width;
   mDisplay.disp_h_full = desktop_height;

   al_set_new_display_option(ALLEGRO_COLOR_SIZE, 32, ALLEGRO_REQUIRE);

   // this is needed to make opengl 4.3 work the same as windows
   // see visual level select, etc..
   al_set_new_display_option(ALLEGRO_SINGLE_BUFFER, 1, ALLEGRO_REQUIRE);

   // check if windowed values are valid
   int th = 100;
   if (disp_x_wind > disp_w_full-th) disp_x_wind = disp_w_full-th;
   if (disp_y_wind > disp_h_full-th) disp_y_wind = disp_h_full-th;
   if (disp_w_wind > disp_w_full) disp_w_wind = disp_w_full;
   if (disp_h_wind > disp_h_full) disp_h_wind = disp_h_full;

   mConfig.save_config();

   int flags = 0;

   if (fullscreen)
   {
      flags = ALLEGRO_FULLSCREEN_WINDOW | ALLEGRO_RESIZABLE;
      al_set_new_display_flags(flags);
      display = al_create_display(disp_w_wind, disp_h_wind);
   }
   else // windowed
   {
      flags = ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE;
      al_set_new_display_flags(flags);
      display = al_create_display(disp_w_wind, disp_h_wind);
   }

   al_set_window_constraints(display, 320, 240, 0, 0);
   al_apply_window_constraints(display, 1);

   mConfig.save_config();

   if(!display)
   {
      mInput.m_err("Error creating display");
      exit(0);
   }
   if (!fullscreen) al_set_window_position(display, disp_x_wind, disp_y_wind);
   if (fullscreen)  al_resize_display(display, disp_w_full, disp_h_full);
   al_acknowledge_resize(display);

   disp_w_curr = al_get_display_width(display);
   disp_h_curr = al_get_display_height(display);
   al_get_window_position(display, &disp_x_curr, &disp_y_curr);

   set_display_transform();
   set_window_title();

   mBitmap.create_bitmaps();
   return 1;
}
Process display change This function is called when: - an event 'ALLEGRO_EVENT_DISPLAY_RESIZE' is received - fullscreen mode is toggled
void mwDisplay::proc_display_change(void)
{
   al_acknowledge_resize(display);                              // important that this is here, later and it does not work as intended
   al_get_window_position(display, &disp_x_curr, &disp_y_curr); // set my local variables with the system ones
   disp_w_curr = al_get_display_width(display);
   disp_h_curr = al_get_display_height(display);
   if (fullscreen)
   {
      disp_x_full = disp_x_curr;
      disp_y_full = disp_y_curr;
      disp_w_full = disp_w_curr;
      disp_h_full = disp_h_curr;
   }
   else
   {
      disp_x_wind = disp_x_curr;
      disp_y_wind = disp_y_curr;
      disp_w_wind = disp_w_curr;
      disp_h_wind = disp_h_curr;
   }
   set_display_transform();
   mBitmap.rebuild_bitmaps();
   mConfig.save();
   set_window_title();
}
Toggle fullscreen mode The F12 key toggles fullscreen mode (or the key can be customized to something else)
if (key[function_key_fullscreen][2])  mDisplay.toggle_fullscreen();

void mwDisplay::toggle_fullscreen(void)
{
   if (fullscreen) proc_display_change_fromfs();
   else            proc_display_change_tofs();
}

void mwDisplay::proc_display_change_tofs(void)
{
   fullscreen = 1;
   // save window position and size before entering fullscreen
   al_get_window_position(display, &disp_x_wind, &disp_y_wind);
   disp_w_wind = al_get_display_width(display);
   disp_h_wind = al_get_display_height(display);
   al_set_display_flag(display, ALLEGRO_FULLSCREEN_WINDOW, fullscreen);
   proc_display_change();
}

void mwDisplay::proc_display_change_fromfs(void)
{
   fullscreen = 0;
   al_set_display_flag(display, ALLEGRO_FULLSCREEN_WINDOW, fullscreen);
   al_acknowledge_resize(display);
   // here is one of the few places I will set xywh from my local variables
   al_resize_display(display, disp_w_wind, disp_h_wind);
   al_set_window_position(display, disp_x_wind, disp_y_wind);
   proc_display_change();
}
Saving window size and position This is automatically done when the display is resized or switched to fullscreen, but not when the window is simply moved. So I also call this when exiting to save window position.
void mwDisplay::save_display_window_position(void)
{
   if (!fullscreen)
   {
      al_get_window_position(display, &disp_x_wind, &disp_y_wind);
      disp_w_wind = al_get_display_width(display);
      disp_h_wind = al_get_display_height(display);
      mConfig.save();
   }
}
Display transform double I use an orthographic transform to double or triple the screen size. This was implemented to address large screen sizes and small text on menus and overlays. display_transform_double = 1; // normal (no double) display_transform_double = 2; // double display_transform_double = 3; // triple
void mwDisplay::auto_set_display_transform_double(void)
{
   display_transform_double = 1;
   if (disp_w_curr > 1023) display_transform_double = 2;
   if (disp_h_curr > 1023) display_transform_double = 2;
   if (disp_w_curr < 1024) display_transform_double = 1;
   if (disp_h_curr < 700)  display_transform_double = 1;
   if (mLoop.help_screens_running)
   {
      if (disp_w_curr > 1279) display_transform_double = 2;
      if (disp_w_curr < 1280) display_transform_double = 1;
   }
}

void mwDisplay::set_saved_display_transform(int sdt)
{
   float old_display_transform_double = display_transform_double;
   saved_display_transform_double = sdt;
   mConfig.save();
   set_display_transform();
   float new_display_transform_double = display_transform_double;
   float sfa = new_display_transform_double/old_display_transform_double;
   scale_factor /= sfa;
   scale_factor_current = scale_factor;
   for (int a=0; a < NUM_MW; a++) mWM.mW[a].set_pos(mWM.mW[a].x1/sfa, mWM.mW[a].y1/sfa); // adjust window positions
}

void mwDisplay::set_display_transform()
{
   if (!saved_display_transform_double) auto_set_display_transform_double();
   else display_transform_double = saved_display_transform_double;
   show_dtd = 80;
   al_set_target_backbuffer(display);
   SCREEN_W = disp_w_curr/display_transform_double;
   SCREEN_H = disp_h_curr/display_transform_double;
   ALLEGRO_TRANSFORM trans;
   al_identity_transform(&trans);
   al_orthographic_transform(&trans, 0, 0, -1.0, SCREEN_W, SCREEN_H, 1.0);
   al_use_projection_transform(&trans);
   mScreen.set_map_var();
}
Then all of my drawing functions throughout the rest of the game that need to know screen size use the prescaled 'SCREEN_W' and 'SCREEN_H' instead of the actual sizes. The mouse_x and mouse_y positions are also scaled like this:
mouse_x = ev.mouse.x / display_transform_double;
mouse_y = ev.mouse.y / display_transform_double;
Cycle display transform double You can cycle display transform double with F11 (or customize to some other key) It can be forced to 1, 2, or 3. Or if it is set to 0, it will be automatically set with the function 'auto_set_display_transform_double()' This value is then saved in the config file. See also 'Settings' -> 'Double' for more options.
if (key[function_key_text_double][2]) mDisplay.cycle_display_transform();
   
void mwDisplay::cycle_display_transform(void)
{
   float old_display_transform_double = display_transform_double;
   if (++saved_display_transform_double>display_transform_double_max) saved_display_transform_double = 0;
   mConfig.save();
   set_display_transform();
   float new_display_transform_double = display_transform_double;
   float sfa = new_display_transform_double/old_display_transform_double;
   scale_factor /= sfa;
   scale_factor_current = scale_factor;
   for (int a=0; a < NUM_MW; a++) mWM.mW[a].set_pos(mWM.mW[a].x1/sfa, mWM.mW[a].y1/sfa); // adjust window positions
}