[AD] Real weird unix bug, help wanted

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Hi,

First a short intro I'm a volunteer software packager for the Fedora Linux Distro and I've recently been packaging allegro using software. I've already send 2 patches for bugs I found to the list, one for digmid and one for alsa sound on bigendian machines.

I've spend my last 2 days debugging a real weird problem and any help/input is appreciated.

There is a problem (under unix using the X11-system driver) when calling
set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, ... when changing from a windowed display to a fullscreen display.

I'm not at all sure that this is allegro's fault it might very well be the brand spanking new modular Xorg 7.0 release I'm using. While we're on the topic, I'm using (and you might need an exact copy to reproduce):
-athlon64 2800+
-radeon 9250 / 9200pro with 256 mb ram
-latest x86_64 (64 bits) version of Fedora (FC5test3 + updates)
-using gnome with the (default) metacity window manager.
-allegro-4.2.0-8 as packaged by Fedora-Extras


Back to the problem, the problem is in src/x/xwin.c around line 734:
   if (fullscreen) {
      AL_CONST char *fc;
      char tmp1[64], tmp2[128];
      int i;

      /* Switch video mode.  */
      if (!_xvidmode_private_set_fullscreen(w, h)) {
         ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("C
         return 0;
      }

      /* Hack: make the window fully visible and center cursor.  */
      XMoveWindow(_xwin.display, _xwin.window, 0, 0);
      XF86VidModeSetViewPort(_xwin.display, _xwin.screen, 0, 0);

      /* This chunk is disabled by default because of problems on KDE
      fc = get_config_string(uconvert_ascii("graphics", tmp1),
                             uconvert_ascii("force_centering", tmp2),
                             NULL);

      if ((fc) && ((i = ugetc(fc)) != 0) && ((i == 'y') || (i == 'Y') ||
         XWarpPointer(_xwin.display, None, _xwin.window, 0, 0, 0, 0, 0,
         XWarpPointer(_xwin.display, None, _xwin.window, 0, 0, 0, 0, w -
         XWarpPointer(_xwin.display, None, _xwin.window, 0, 0, 0, 0, 0,
         XWarpPointer(_xwin.display, None, _xwin.window, 0, 0, 0, 0, w -
      }

      XWarpPointer(_xwin.display, None, _xwin.window, 0, 0, 0, 0, w / 2,
      XSync(_xwin.display, False);

      /* Grab the keyboard and mouse.  */
      if (XGrabKeyboard(_xwin.display, XDefaultRootWindow(_xwin.display)
                        GrabModeAsync, GrabModeAsync, CurrentTime) != Gr
         ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("C
         return 0;
      }
      _xwin.keyboard_grabbed = 1;
      if (XGrabPointer(_xwin.display, _xwin.window, False,
                       PointerMotionMask | ButtonPressMask |
                       GrabModeAsync, GrabModeAsync, _xwin.window, None,
         ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Ca
         return 0;
      }
      _xwin.mouse_grabbed = 1;
   }


This should create an unmanaged window with its left top at position 0.0 of the root window. Now the problem is that with one allegro using program, sometimes it is as if the XMoveWindow call and the XGrabPointer call fail silently. In this case the window is at its last position (before the set_gfx_mode call) and the mouse is ungrabbed, thus I can pan around the rest of my desktop. It is as if both the move and the mousegrab never happened. Strange enough the keyboardgrab has happened.

I've attached a very much minimized version of the program causing this problem and I've also found a workaround. There is a define called workaround in the top of the file, comment this to get the buggy behaviour. Once you have the file compiled with the workaround define commented press any key todo the fullscreen<->window toggle. Within say 10 toggle you should see the unwanted behaviour of the window not being at 0.0 (the viewport will be there) and then you can also freely cause the mouse to move outside the window and thus the viewport to scroll.

The workaround consists of waiting for the key which was pressed to be released before doing the switch. Don't ask me why but this fixes it (it took me ages (hours) to find this, looking at how other programs without the problem did it).

I've also written an alternative workaround which works from within allegro, add the following lines right before the XMoveWindow call:
      /* HACK HACK HACK, XMoveWindow and XGrabPointer seem to fail
         (silently!)  when called on a window when keys are still
         pressed. */
      do {
        char keys[32];
        XQueryKeymap(_xwin.display, keys);
        for (i=0; (i<32) && (keys[i]==0); i++) {}
      } while(i<32);

      /* Hack: make the window fully visible and center cursor.  */
      XMoveWindow(_xwin.display, _xwin.window, 0, 0);


Notice that I've also put the XMoveWindow call above for clearity, you only need this once! I do not advocate this fix as a good one, nor advice to include it in allegro. Its just extra info.

I hope someone can help (if anyone can reproduce this please let me know, that alone would be great, esp with an other X-server (version) then we can rule out certain things).

I know the attached test-case isn't all that simple but it is as good as it gets, its much smaller then the original game having the problem.


Thanks & Regards,

Hans
#include <allegro.h>
#include <stdlib.h>
#include <stdio.h>

#define GRAIN 1000

// comment this to get the bug
// #define workaround

int grand(int number)
{
 if (number == 0)
  return 0;
 return random() % number;
}

BITMAP *menu_bmp;

RGB palet [256];
enum
{
COLOUR_BLACK = 192,
COLOUR_GREY1,
COLOUR_GREY2,
COLOUR_GREY3,
COLOUR_GREY4,
COLOUR_GREY5,
COLOUR_GREY6,
//COLOUR_GREY7,
COLOUR_WHITE,
COLOUR_ORANGE1,
COLOUR_ORANGE2,
COLOUR_ORANGE3,
COLOUR_ORANGE4,
COLOUR_ORANGE5,
COLOUR_ORANGE6,
COLOUR_ORANGE7,
COLOUR_ORANGE8,
COLOUR_YELLOW1,
COLOUR_YELLOW2,
COLOUR_YELLOW3,
COLOUR_YELLOW4,
COLOUR_YELLOW5,
COLOUR_YELLOW6,
COLOUR_YELLOW7,
COLOUR_YELLOW8,
COLOUR_RED1,
COLOUR_RED2,
COLOUR_RED3,
COLOUR_RED4,
COLOUR_RED5,
COLOUR_RED6,
COLOUR_RED7,
COLOUR_RED8,
COLOUR_GREEN1,
COLOUR_GREEN2,
COLOUR_GREEN3,
COLOUR_GREEN4,
COLOUR_GREEN5,
COLOUR_GREEN6,
COLOUR_GREEN7,
COLOUR_GREEN8,
COLOUR_BLUE1,
COLOUR_BLUE2,
COLOUR_BLUE3,
COLOUR_BLUE4,
COLOUR_BLUE5,
COLOUR_BLUE6,
COLOUR_BLUE7,
COLOUR_BLUE8,
COLOUR_PURPLE1,
COLOUR_PURPLE2,
COLOUR_PURPLE3,
COLOUR_PURPLE4,
COLOUR_PURPLE5,
COLOUR_PURPLE6,
COLOUR_PURPLE7,
COLOUR_PURPLE8,
COLOUR_X1,
COLOUR_X2,
COLOUR_X3,
COLOUR_X4,
COLOUR_X5,
COLOUR_X6,
COLOUR_X7,
COLOUR_X8
//COLOUR_WRITING,
//COLOUR_BROWN

};

int base_palette [64] [3] =
{
{0, 0, 0},
{0, 0, 1},
{10, 10, 10},
{20, 20, 20},
{30, 30, 30},
{40, 40, 40},
{50, 50, 50},
{63, 63, 63}, // greys
{8, 3, 0},
{16, 6, 0},
{24, 9, 0},
{32, 12, 0},
{40, 15, 0},
{48, 18, 0},
{56, 21, 0},
{63, 24, 0}, // Oranges
{8, 8, 0},
{16, 16, 0},
{24, 24, 4},
{32, 32, 8},
{40, 40, 12},
{48, 48, 16},
{56, 56, 20},
{63, 63, 24}, // yellows
{8, 0, 0},
{16, 0, 0},
{24, 0, 0},
{32, 3, 3},
{40, 5, 5},
{48, 7, 7},
{56, 9, 9},
{63, 11, 11}, // Reds
{0, 8, 0},
{0, 16, 0},
{3, 24, 3},
{6, 32, 6},
{8, 40, 8},
{12, 48, 12},
{16, 56, 16},
{20, 63, 20}, // Greens
{0, 0, 8},
{0, 0, 16},
{4, 4, 24},
{8, 8, 32},
{12, 12, 40},
{16, 16, 48},
{20, 20, 56},
{24, 24, 63}, // Blues
{8, 0, 8},
{16, 0, 16},
{24, 0, 24},
{32, 0, 32},
{40, 0, 40},
{48, 0, 48},
{56, 0, 56},
{63, 0, 63}, // Purples
{8, 3, 8},
{16, 5, 16},
{24, 9, 24},
{32, 12, 32},
{40, 34, 40},
{48, 40, 48},
{56, 50, 56},
{63, 55, 63}, // X colours

};



int limit_colour(int colour_input)
{

 if (colour_input < 0) return 0;
 if (colour_input > 63) return 63;
 return colour_input;

}

enum
{
TRANS_WHITE = 195,
TRANS_PURPLE, // 196
TRANS_LBLUE, // 197
TRANS_DBLUE, // 198
TRANS_YELLOW, // 199
TRANS_LGREEN, // 200
TRANS_DGREEN, // 201
TRANS_LORANGE, // 202
TRANS_DORANGE, // 203
TRANS_LRED, // 204
TRANS_DRED, // 205
TRANS_WHITE2, // 206
// Special white which doesn't overwrite other colours (for shockwaves etc)
TRANS_LGREY, // 207
TRANS_DGREY, // 208

TRANS_GREY, // 209
// GREY is special. It's the only transparency which preserves colour, not
//  just brightness, and it takes up four times as much space in the
//  palette as the others. WHITE, LGREY and DGREY are just normal
//  transparencies, and both will overwrite GREY.
// GREY should be last, but for:
TRANS_END //207
// must be the last trans + 1
};

void init_palette(void)
{

int i;


// colour_table("First");

 for (i = 0; i < 64; i ++)
 {
    // Grey
    palet[i + 128].r = limit_colour(base_palette [i] [0] + 20);
    palet[i + 128].g = limit_colour(base_palette [i] [1] + 20);
    palet[i + 128].b = limit_colour(base_palette [i] [2] + 20);

    palet[i + 192].r = base_palette [i] [0];
    palet[i + 192].g = base_palette [i] [1];
    palet[i + 192].b = base_palette [i] [2];

    
  }

 for (i = 0; i < 8; i ++)
 {
    // White - should be same as WHITE2 (below)
//    palet[i].r = limit_colour(32 + i * 4);
//    palet[i].g = limit_colour(32 + i * 4);
//    palet[i].b = limit_colour(32 + i * 4);
    palet[i].r = limit_colour(48 + i * 2);
    palet[i].g = limit_colour(48 + i * 2);
    palet[i].b = limit_colour(48 + i * 2);

    // Purple
    palet[i + 8].r = limit_colour(32 + i * 4);
    palet[i + 8].g = limit_colour(i * 3);
    palet[i + 8].b = limit_colour(32 + i * 4);

    // LBlue
    palet[i + 16].r = limit_colour(10 + i * 3);
    palet[i + 16].g = limit_colour(10 + i * 3);
    palet[i + 16].b = limit_colour(47 + i * 2);

    // DBlue
    palet[i + 24].r = limit_colour(i * 2);
    palet[i + 24].g = limit_colour(i * 2);
    palet[i + 24].b = limit_colour(21 + i * 4);

    // Yellow
    palet[i + 32].r = limit_colour(47 + i * 3);
    palet[i + 32].g = limit_colour(47 + i * 3);
    palet[i + 32].b = limit_colour(10 + i * 3);

    // LGreen
    palet[i + 40].r = limit_colour(i * 3);
    palet[i + 40].g = limit_colour(32 + i * 4);
    palet[i + 40].b = limit_colour(i * 3);

    // DGreen
    palet[i + 48].r = limit_colour(i * 2);
    palet[i + 48].g = limit_colour(21 + i * 4);
    palet[i + 48].b = limit_colour(i * 2);

    // LOrange
    palet[i + 56].r = limit_colour(47 + i * 3);
    palet[i + 56].g = limit_colour(20 + i * 3);
    palet[i + 56].b = limit_colour(5 + i * 2);

    // DOrange
    palet[i + 64].r = limit_colour(25 + i * 4);
    palet[i + 64].g = limit_colour(12 + i * 3);
    palet[i + 64].b = limit_colour(i * 2);

    // LRed
    palet[i + 72].r = limit_colour(47 + i * 2);
    palet[i + 72].g = limit_colour(10 + i * 3);
    palet[i + 72].b = limit_colour(10 + i * 3);

    // DRed
    palet[i + 80].r = limit_colour(20 + i * 4);
    palet[i + 80].g = limit_colour(i * 2);
    palet[i + 80].b = limit_colour(i * 2);

    // White2 - should be same as WHITE (above)
//    palet[i + 88].r = limit_colour(32 + i * 4);
//    palet[i + 88].g = limit_colour(32 + i * 4);
//    palet[i + 88].b = limit_colour(32 + i * 4);
    palet[i + 88].r = limit_colour(48 + i * 2);
    palet[i + 88].g = limit_colour(48 + i * 2);
    palet[i + 88].b = limit_colour(48 + i * 2);

    // LGrey
    palet[i + 96].r = limit_colour(24 + i * 4);
    palet[i + 96].g = limit_colour(24 + i * 4);
    palet[i + 96].b = limit_colour(24 + i * 4);

    // DGrey
    palet[i + 104].r = limit_colour(12 + i * 4);
    palet[i + 104].g = limit_colour(12 + i * 4);
    palet[i + 104].b = limit_colour(12 + i * 4);


/*    // White
    palet[i].r = limit_colour(21 + i * 6);
    palet[i].g = limit_colour(21 + i * 6);
    palet[i].b = limit_colour(21 + i * 6);

    // Purple
    palet[i + 8].r = limit_colour(21 + i * 6);
    palet[i + 8].g = limit_colour(i * 3 - 12);
    palet[i + 8].b = limit_colour(21 + i * 6);

    // LBlue
    palet[i + 16].r = limit_colour(i * 4 - 12);
    palet[i + 16].g = limit_colour(i * 4 - 12);
    palet[i + 16].b = limit_colour(21 + i * 6);

    // DBlue
    palet[i + 24].r = limit_colour(i * 2 - 12);
    palet[i + 24].g = limit_colour(i * 2 - 12);
    palet[i + 24].b = limit_colour(10 + i * 5);

    // LGreen
    palet[i + 32].r = limit_colour(i * 4 - 12);
    palet[i + 32].g = limit_colour(21 + i * 6);
    palet[i + 32].b = limit_colour(i * 4 - 12);

    // DGreen
    palet[i + 40].r = limit_colour(i * 2 - 12);
    palet[i + 40].g = limit_colour(10 + i * 5);
    palet[i + 40].b = limit_colour(i * 2 - 12);

    // Yellow
    palet[i + 48].r = limit_colour(21 + i * 6);
    palet[i + 48].g = limit_colour(21 + i * 6);
    palet[i + 48].b = limit_colour(i * 4 - 12);

    // LOrange
    palet[i + 56].r = limit_colour(21 + i * 6);
    palet[i + 56].g = limit_colour(12 + i * 5);
    palet[i + 56].b = limit_colour(i * 4 - 12);

    // DOrange
    palet[i + 64].r = limit_colour(10 + i * 5);
    palet[i + 64].g = limit_colour(6 + i * 4);
    palet[i + 64].b = limit_colour(i * 4 - 12);

    // LRed
    palet[i + 72].r = limit_colour(21 + i * 6);
    palet[i + 72].g = limit_colour(i * 4 - 12);
    palet[i + 72].b = limit_colour(i * 4 - 12);

    // DRed
    palet[i + 80].r = limit_colour(10 + i * 5);
    palet[i + 80].g = limit_colour(i * 2 - 12);
    palet[i + 80].b = limit_colour(i * 2 - 12);
*/
 }

  palet[0].r = 0;
  palet[0].g = 0;
  palet[0].b = 0;


/*  for (i = 0; i < 192; i ++)
  {
   palet[i].r = 0;
   palet[i].b = 0;
   palet[i].g = 0;
  }*/



/*   pork_create_color_table(&trans_table, palet);

   color_map = &trans_table;

   set_palette(palet); */
/*
   BITMAP *pbmp = create_bitmap(16, 16);

   int x, y;

   for (x = 0; x < 16; x ++)
   {
    for (y = 0; y < 16; y ++)
    {
     putpixel(pbmp, y, x, y + (x * 16));
     if (y + (x * 16) < 192)
      putpixel(pbmp, y, x, 0);
    }
   }

   save_bitmap("palbmp3.pcx", pbmp, palet);

   for (x = 192; x < 256; x ++)
   {
    rectfill(screen, (x - 192) * 6, 1, (x - 192) * 6 + 6, 10, x);
   }

   do
   {

   } while (key [KEY_U] == 0);
*/
// int j;

/* for (i = 0; i < 256; i ++)
 {
  j = limit_colour((palet[i].r + palet[i].g + palet[i].b) / 3);
  mono_palet [i].r = j;
  mono_palet [i].g = j;
  mono_palet [i].b = j;
 }

 mono_palet [222].r = 60;
 mono_palet [222].g = 40;
 mono_palet [222].b = 0;
 mono_palet [223].r = 0;
 mono_palet [223].g = 60;
 mono_palet [223].b = 10; // these colours used for 'you win'/'game over'
*/
}

int grid_x_speed, grid_y_speed, grid_x, grid_y, grid_x_accel, grid_y_accel;

void draw_scrolling_grid(int min_x, int min_y, int max_x, int max_y, int colour)
{

 int i, j, x1, y1;

 int x_offset = (grid_x / GRAIN) % 50;
 int y_offset = (grid_y / GRAIN) % 50;

 for (i = 0; i < 15; i ++)
 {
   x1 = i * 50 + x_offset;
   if (x1 < min_x || x1 > max_x)
    continue;
   vline(menu_bmp, x1, min_y, max_y, colour);
 }
 for (j = 0; j < 12; j ++)
 {
   y1 = j * 50 + y_offset;
   if (y1 < min_y || y1 > max_y)
    continue;
   hline(menu_bmp, min_x, y1, max_x, colour);
 }

}


void make_grid_scroll(void)
{

 grid_x += grid_x_speed;
 grid_y += grid_y_speed;

 grid_x %= 50000;
 grid_y %= 50000;

 grid_x_speed += grid_x_accel;
 if (grid_x_speed > 3000) grid_x_speed = 3000;
 if (grid_x_speed < -3000) grid_x_speed = -3000;
 grid_y_speed += grid_y_accel;
  if (grid_y_speed > 3000) grid_y_speed = 3000;
  if (grid_y_speed < -3000) grid_y_speed = -3000;

// if (menu_counter % 32 == 0)
 {
  grid_x_accel = grand(301) - 150;
//  if (grid_x_speed > 3000) grid_x_speed = 3000;
//  if (grid_x_speed < -3000) grid_x_speed = -3000;

  grid_y_accel = grand(301) - 150;
//  if (grid_y_speed > 3000) grid_y_speed = 3000;
//  if (grid_y_speed < -3000) grid_y_speed = -3000;
 }

}

void show_grid(int grid_colour, int border_colour)
{

  rectfill(menu_bmp, 10, 10, 630, 470, COLOUR_GREY1);
  rect(menu_bmp, 10, 10, 630, 470, border_colour - 4);
  rect(menu_bmp, 9, 9, 631, 471, border_colour - 2);
  rect(menu_bmp, 8, 8, 632, 472, border_colour);

  draw_scrolling_grid(11, 11, 629, 469, grid_colour);

}


volatile unsigned char ticked;

void tickover(void)
{
 ticked ++;
// tick_counter++; // assumes it'll wrap at 256
}
END_OF_FUNCTION (tickover);

int main (void)
{
   int i;
   allegro_init();
   install_keyboard();

   LOCK_FUNCTION (tickover);
   LOCK_VARIABLE (ticked);

   install_int (tickover, 30);  
   init_palette();

   set_color_depth(8);
   menu_bmp = create_bitmap(640, 480);

   while(1) {
      if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0) {
         printf("fout: %s\n", allegro_error);
         exit(1);
      }
      set_palette(palet);
      do {
        while(ticked == 0)
         rest(1);
        ticked --;
        make_grid_scroll();
        clear_bitmap(menu_bmp);
        show_grid(COLOUR_GREEN3, COLOUR_GREEN8);
        acquire_screen();
        blit(menu_bmp, screen, 0, 0, 0, 0, 640, 480);
        release_screen();
        for (i = 0; i < 128; i++)
          if (key[i])
            break;
      } while (i == 128);
#ifdef workaround      
      while (key[i]) {}
#endif
      if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0) != 0) {
         printf("fout: %s\n", allegro_error);
         exit(1);
      }
      set_palette(palet);
      do {
        while(ticked == 0)
         rest(1);
        ticked --;
        make_grid_scroll();
        clear_bitmap(menu_bmp);
        show_grid(COLOUR_GREEN3, COLOUR_GREEN8);
        acquire_screen();
        blit(menu_bmp, screen, 0, 0, 0, 0, 640, 480);
        release_screen();
        for (i = 0; i < 128; i++)
          if (key[i])
            break;
      } while (i == 128);
#ifdef workaround      
      while (key[i]) {}
#endif
   }
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/