Skip to content

Commit

Permalink
add al_get_window_borders() and implement for X11 (liballeg#1497)
Browse files Browse the repository at this point in the history
Add al_get_window_borders() and implement for X11

Co-authored-by: Elias Pschernig <[email protected]>
  • Loading branch information
elias-pschernig and allefant authored Oct 29, 2023
1 parent b0d323b commit 11a6425
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 36 deletions.
18 changes: 16 additions & 2 deletions docs/src/refman/display.txt
Original file line number Diff line number Diff line change
Expand Up @@ -552,13 +552,27 @@ See also: [al_resize_display], [ALLEGRO_EVENT]

Gets the position of a non-fullscreen display.

See also: [al_set_window_position]
See also: [al_set_window_position], [al_get_window_borders]

### API: al_set_window_position

Sets the position on screen of a non-fullscreen display.

See also: [al_get_window_position]
See also: [al_get_window_position], [al_get_window_borders]

### API: al_get_window_borders

If that information is available returns TRUE and fills in the size of
the window borders. You can pass NULL for borders you do not want to
retrieve.

If the border information is not available returns FALSE.

Note: Currently only the X11 version will report window borders.

See also: [al_set_window_position], [al_get_window_position]

Since: 5.2.9

### API: al_get_window_constraints

Expand Down
80 changes: 59 additions & 21 deletions examples/ex_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ int main(int argc, char **argv)
}

al_install_mouse();
al_install_keyboard();
al_init_font_addon();
open_log();

Expand All @@ -48,23 +49,26 @@ int main(int argc, char **argv)
log_printf("Monitor %d: %d %d - %d %d\n", i, info[i].x1, info[i].y1, info[i].x2, info[i].y2);
}

// center the first window
ALLEGRO_MONITOR_INFO init_info = info[0];
x = init_info.x1 + ((init_info.x2 - init_info.x1) / 3) - (W / 2);
y = init_info.y1 + ((init_info.y2 - init_info.y1) / 2) - (H / 2);
x = (init_info.x1 + init_info.x2 - W) / 2;
y = (init_info.y1 + init_info.y2 - H) / 2;
jump_x[0] = x;
jump_y[0] = y;
jump_adapter[0] = 0;

al_set_new_window_position(x, y);
al_set_new_window_title("Window 1");

al_set_new_display_flags(ALLEGRO_RESIZABLE);
displays[0] = al_create_display(W, H);

x = init_info.x1 + (2 * (init_info.x2 - init_info.x1) / 3) - (W / 2);
jump_x[1] = x;
jump_y[1] = y;
// use the default position for the second window
jump_x[1] = INT_MAX;
jump_y[1] = INT_MAX;
jump_adapter[1] = 0;
al_set_new_window_position(x, y);
al_set_new_window_position(INT_MAX, INT_MAX);
al_set_new_window_title("Window 2");

displays[1] = al_create_display(W, H);

Expand All @@ -80,6 +84,7 @@ int main(int argc, char **argv)
al_register_event_source(events, al_get_display_event_source(displays[0]));
al_register_event_source(events, al_get_display_event_source(displays[1]));
al_register_event_source(events, al_get_timer_event_source(timer));
al_register_event_source(events, al_get_keyboard_event_source());

bool redraw = true;
al_start_timer(timer);
Expand All @@ -97,10 +102,24 @@ int main(int argc, char **argv)
al_get_window_position(displays[i], &dx, &dy);
dw = al_get_display_width(displays[i]);
dh = al_get_display_height(displays[i]);
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 - 15, ALLEGRO_ALIGN_CENTRE, "Location: %d %d", dx, dy);
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2, ALLEGRO_ALIGN_CENTRE, "Last jumped to: %d %d (adapter %d)", jump_x[i], jump_y[i], jump_adapter[i]);
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 15, ALLEGRO_ALIGN_CENTRE, "Size: %dx%d", dw, dh);
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 30, ALLEGRO_ALIGN_CENTRE, "Click me to jump!");
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 - 30, ALLEGRO_ALIGN_CENTRE, "Location: %d %d", dx, dy);
if (jump_x[i] != INT_MAX && jump_y[i] != INT_MAX) {
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 - 15, ALLEGRO_ALIGN_CENTRE,
"Last jumped to: %d %d (adapter %d)", jump_x[i], jump_y[i], jump_adapter[i]);
}
else {
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 - 15, ALLEGRO_ALIGN_CENTRE,
"Last placed to default position (adapter %d)", jump_adapter[i]);
}
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 0, ALLEGRO_ALIGN_CENTRE, "Size: %dx%d", dw, dh);
int bl = 0, bt = 0;
bool b = al_get_window_borders(displays[i], &bl, &bt, NULL, NULL);
if (b)
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 15, ALLEGRO_ALIGN_CENTRE, "Borders: left=%d top=%d", bl, bt);
else
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 15, ALLEGRO_ALIGN_CENTRE, "Borders: unknown");
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 30, ALLEGRO_ALIGN_CENTRE, "Click left to jump!");
al_draw_textf(myfont, al_map_rgb(0, 0, 0), dw / 2, dh / 2 + 45, ALLEGRO_ALIGN_CENTRE, "Click right to swap!");

al_flip_display();
}
Expand All @@ -112,17 +131,36 @@ int main(int argc, char **argv)
break;
}
else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
int a = rand() % adapter_count;
int w = info[a].x2 - info[a].x1;
int h = info[a].y2 - info[a].y1;
int margin = 20;
int i = event.mouse.display == displays[0] ? 0 : 1;
x = margin + info[a].x1 + (rand() % (w - W - 2 * margin));
y = margin + info[a].y1 + (rand() % (h - H - 2 * margin));
jump_x[i] = x;
jump_y[i] = y;
jump_adapter[i] = a;
al_set_window_position(event.mouse.display, x, y);
if (event.mouse.button == 1) {
int a = rand() % adapter_count;
int w = info[a].x2 - info[a].x1;
int h = info[a].y2 - info[a].y1;
int margin = 20;
int i = event.mouse.display == displays[0] ? 0 : 1;
x = margin + info[a].x1 + (rand() % (w - W - 2 * margin));
y = margin + info[a].y1 + (rand() % (h - H - 2 * margin));
jump_x[i] = x;
jump_y[i] = y;
jump_adapter[i] = a;
log_printf("moving window %d to %d/%d\n", 1 + i, x, y);
al_set_window_position(event.mouse.display, x, y);
}
else {
log_printf("swapping windows\n");
al_get_window_position(displays[0], jump_x + 1, jump_y + 1);
al_get_window_position(displays[1], jump_x, jump_y);
al_set_window_position(displays[0], jump_x[0], jump_y[0]);
al_set_window_position(displays[1], jump_x[1], jump_y[1]);
}
}
else if (event.type == ALLEGRO_EVENT_KEY_DOWN) {
if (event.keyboard.keycode == ALLEGRO_KEY_SPACE) {
al_get_window_position(event.keyboard.display, &x, &y);
int i = event.mouse.display == displays[0] ? 0 : 1;
jump_x[i] = x;
jump_y[i] = y;
al_set_window_position(event.keyboard.display, x, y);
}
}
else if (event.type == ALLEGRO_EVENT_TIMER) {
redraw = true;
Expand Down
1 change: 1 addition & 0 deletions include/allegro5/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ AL_FUNC(void, al_set_new_window_position, (int x, int y));
AL_FUNC(void, al_get_new_window_position, (int *x, int *y));
AL_FUNC(void, al_set_window_position, (ALLEGRO_DISPLAY *display, int x, int y));
AL_FUNC(void, al_get_window_position, (ALLEGRO_DISPLAY *display, int *x, int *y));
AL_FUNC(bool, al_get_window_borders, (ALLEGRO_DISPLAY *display, int *left, int *top, int *right, int *bottom));
AL_FUNC(bool, al_set_window_constraints, (ALLEGRO_DISPLAY *display, int min_w, int min_h, int max_w, int max_h));
AL_FUNC(bool, al_get_window_constraints, (ALLEGRO_DISPLAY *display, int *min_w, int *min_h, int *max_w, int *max_h));
AL_FUNC(void, al_apply_window_constraints, (ALLEGRO_DISPLAY *display, bool onoff));
Expand Down
1 change: 1 addition & 0 deletions include/allegro5/internal/aintern_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct ALLEGRO_DISPLAY_INTERFACE

void (*set_window_position)(ALLEGRO_DISPLAY *display, int x, int y);
void (*get_window_position)(ALLEGRO_DISPLAY *display, int *x, int *y);
bool (*get_window_borders)(ALLEGRO_DISPLAY *display, int *left, int *top, int *right, int *bottom);
bool (*set_window_constraints)(ALLEGRO_DISPLAY *display, int min_w, int min_h, int max_w, int max_h);
bool (*get_window_constraints)(ALLEGRO_DISPLAY *display, int *min_w, int *min_h, int *max_w, int *max_h);
bool (*set_display_flag)(ALLEGRO_DISPLAY *display, int flag, bool onoff);
Expand Down
3 changes: 3 additions & 0 deletions include/allegro5/internal/aintern_xdisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ struct ALLEGRO_DISPLAY_XGLX

/* Desktop position. */
int x, y;
bool need_initial_position_adjust;
int border_left, border_right, border_top, border_bottom;
bool borders_known;

/* al_set_mouse_xy implementation */
bool mouse_warp;
Expand Down
1 change: 1 addition & 0 deletions include/allegro5/internal/aintern_xwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ void _al_xwin_set_icons(ALLEGRO_DISPLAY *d,
int num_icons, ALLEGRO_BITMAP *bitmaps[]);
void _al_xwin_maximize(ALLEGRO_DISPLAY *d, bool maximized);
void _al_xwin_check_maximized(ALLEGRO_DISPLAY *display);
void _al_xwin_get_borders(ALLEGRO_DISPLAY *display);

#endif

Expand Down
13 changes: 13 additions & 0 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,19 @@ void al_get_window_position(ALLEGRO_DISPLAY *display, int *x, int *y)
}


/* Function: al_get_window_borders
*/
bool al_get_window_borders(ALLEGRO_DISPLAY *display, int *left, int *top, int *right, int *bottom)
{
if (display && display->vt && display->vt->get_window_borders) {
return display->vt->get_window_borders(display, left, top, right, bottom);
}
else {
return false;
}
}


/* Function: al_set_window_constraints
*/
bool al_set_window_constraints(ALLEGRO_DISPLAY *display,
Expand Down
63 changes: 50 additions & 13 deletions src/x/xdisplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ static bool xdpy_create_display_window(ALLEGRO_SYSTEM_XGLX *system,
int xscr_x = 0;
int xscr_y = 0;
al_get_new_window_position(&x_off, &y_off);
if (x_off != INT_MAX && y_off != INT_MAX) {
d->need_initial_position_adjust = true;
ALLEGRO_DEBUG("Will adjust window position later to %d/%d\n", x_off, y_off);
}

if (adapter >= 0) {
/* Non default adapter. I'm assuming this means the user wants the
Expand Down Expand Up @@ -1038,6 +1042,8 @@ void _al_xglx_display_configure(ALLEGRO_DISPLAY *d, int x, int y,
ALLEGRO_EVENT_SOURCE *es = &glx->display.es;
_al_event_source_lock(es);

_al_xwin_get_borders(d);

/* Generate a resize event if the size has changed non-programmatically.
* We cannot asynchronously change the display size here yet, since the user
* will only know about a changed size after receiving the resize event.
Expand All @@ -1059,8 +1065,28 @@ void _al_xglx_display_configure(ALLEGRO_DISPLAY *d, int x, int y,
}

if (setglxy) {
// Note: XConfigure events will have our inner window position
// which is what we are using for al_set/get_window_position
glx->x = x;
glx->y = y;

if (glx->need_initial_position_adjust && glx->borders_known) {
glx->need_initial_position_adjust = false;
// Unfortunately there doesn't seem to be a good way to know the
// border size before mapping the window, so we have to adjust
// the position slightly now, after the window is mapped.
// The scenario is this:
// 1. We create the window, passing the user position to X11, which
// is interpreted as outer coordinates. Since borders are
// not known yet d->x/d->y are also set to that position.
// 2. The window is mapped and X11 sends us the inner coordinates
// in XConfigure which we store in d->x/d->y.
// 3. Now d->x/d->y is in sync with the real position, but if
// we wanted a specific position it is off by the border size
// and so we adjust it below.
ALLEGRO_DEBUG("Adjusting initial position: %d/%d", glx->x - glx->border_left, glx->y - glx->border_top);
al_set_window_position(d, glx->x - glx->border_left, glx->y - glx->border_top);
}
}

ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX*)al_get_system_driver();
Expand Down Expand Up @@ -1223,31 +1249,29 @@ static void xdpy_set_window_title(ALLEGRO_DISPLAY *display, const char *title)
}


// Note: we assume x/y to be the inner window position (that is drawing
// to 0/0 in Allegro's drawing area will draw to x/y on the desktop)
static void xdpy_set_window_position_default(ALLEGRO_DISPLAY *display,
int x, int y)
{
ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
Window root, parent, child, *children;
unsigned int n;

_al_mutex_lock(&system->lock);

/* To account for the window border, we have to find the parent window which
* draws the border. If the parent is the root though, then we should not
* translate.
*/
XQueryTree(system->x11display, glx->window, &root, &parent, &children, &n);
if (parent != root) {
XTranslateCoordinates(system->x11display, parent, glx->window,
x, y, &x, &y, &child);
}
// Note: a previous version of this function tried to translate the
// coordinates with XTranslateCoordinates (assuming a parent window
// offset by exactly the border) - but that didn't quite work in any
// of the window managers I tried.

XMoveWindow(system->x11display, glx->window, x, y);
// XMoveWindow expects outer coordinates so we offset by the border
// size.
XMoveWindow(system->x11display, glx->window, x - glx->border_left,
y - glx->border_top);
XFlush(system->x11display);

/* We have to store these immediately, as we will ignore the XConfigureEvent
* that we receive in response. _al_display_xglx_configure() knows why.
* that we receive in response. See comments in _al_xglx_display_configure().
*/
glx->x = x;
glx->y = y;
Expand Down Expand Up @@ -1275,6 +1299,18 @@ static void xdpy_get_window_position(ALLEGRO_DISPLAY *display, int *x, int *y)
}


static bool xdpy_get_window_borders(ALLEGRO_DISPLAY *display, int *left, int *top, int *right, int *bottom)
{
ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
if (!glx->borders_known) return false;
if (left) *left = glx->border_left;
if (right) *right = glx->border_right;
if (top) *top = glx->border_top;
if (bottom) *bottom = glx->border_bottom;
return true;
}


static bool xdpy_set_window_constraints_default(ALLEGRO_DISPLAY *display,
int min_w, int min_h, int max_w, int max_h)
{
Expand Down Expand Up @@ -1420,6 +1456,7 @@ ALLEGRO_DISPLAY_INTERFACE *_al_display_xglx_driver(void)
xdpy_vt.set_window_title = xdpy_set_window_title;
xdpy_vt.set_window_position = xdpy_set_window_position;
xdpy_vt.get_window_position = xdpy_get_window_position;
xdpy_vt.get_window_borders = xdpy_get_window_borders;
xdpy_vt.set_window_constraints = xdpy_set_window_constraints;
xdpy_vt.get_window_constraints = xdpy_get_window_constraints;
xdpy_vt.apply_window_constraints = xdpy_apply_window_constraints;
Expand Down
34 changes: 34 additions & 0 deletions src/x/xwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,38 @@ XID al_get_x_window_id(ALLEGRO_DISPLAY *display)
return ((ALLEGRO_DISPLAY_XGLX*)display)->window;
}


// Note: this only seems to work after the window has been mapped
void _al_xwin_get_borders(ALLEGRO_DISPLAY *display) {
ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
Display *x11 = system->x11display;

Atom type;
int format;
unsigned long nitems, bytes_after;
unsigned char *property;
Atom frame_extents = X11_ATOM(_NET_FRAME_EXTENTS);
if (XGetWindowProperty(x11, glx->window,
frame_extents, 0, 16, 0, XA_CARDINAL,
&type, &format, &nitems, &bytes_after, &property) == Success) {
if (type != None && nitems == 4) {
glx->border_left = (int)((long *)property)[0];
glx->border_right = (int)((long *)property)[1];
glx->border_top = (int)((long *)property)[2];
glx->border_bottom = (int)((long *)property)[3];
glx->borders_known = true;
ALLEGRO_DEBUG("_NET_FRAME_EXTENTS: %d %d %d %d\n", glx->border_left, glx->border_top,
glx->border_right, glx->border_bottom);
}
else {
ALLEGRO_DEBUG("Unexpected _NET_FRAME_EXTENTS format: nitems=%lu\n", nitems);
}
XFree(property);
}
else {
ALLEGRO_DEBUG("Could not read _NET_FRAME_EXTENTS\n");
}
}

/* vim: set sts=3 sw=3 et: */

0 comments on commit 11a6425

Please sign in to comment.