1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-05-01 02:31:23 -07:00

Add functions to set frame size and position in one compound step

* lisp/frame.el (set-frame-size-and-position): New function.
* src/frame.c (adjust_frame_size): Handle requests to set size
and position.
(Fset_frame_size_and_position_pixelwise): New function.
* src/gtkutil.c (xg_frame_set_size_and_position): New function.
(xg_wm_set_size_hint): Handle any non-NorthWestGravity values
for child frames only.  Some GTK implementations don't like
them.
* src/gtkutil.h (xg_frame_set_size_and_position.): Add external
declaration.
* src/termhooks.h (set_window_size_and_position_hook): New hook.
* src/w32term.c (w32_set_window_size_and_position): New
function.
(w32_create_terminal): Make it the Microsoft Windows API
set_window_size_and_position_hook.
* src/xterm.c (x_set_window_size_and_position_1)
(x_set_window_size_and_position): New functions.
(x_create_terminal): Make x_set_window_size_and_position the
set_window_size_and_position_hook for the X protocol.
* src/xterm.h (x_set_window_size_and_position): Add external
declaration.
* etc/NEWS: Announce new functions.
This commit is contained in:
Martin Rudalics 2025-12-22 11:01:53 +01:00
parent 5cf8af2290
commit f3d9371a89
9 changed files with 505 additions and 23 deletions

View file

@ -777,6 +777,7 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
int old_text_width = FRAME_TEXT_WIDTH (f);
int old_text_height = FRAME_TEXT_HEIGHT (f);
bool inhibit_horizontal, inhibit_vertical;
bool size_and_position = EQ (parameter, Qsize_and_position);
Lisp_Object frame;
XSETFRAME (frame, f);
@ -855,7 +856,8 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
/* For inhibit == 1 call the window_size_hook only if a native
size changes. For inhibit == 0 or inhibit == 2 always call
it. */
&& ((!inhibit_horizontal
&& (size_and_position
|| (!inhibit_horizontal
&& (new_native_width != old_native_width
|| inhibit == 0 || inhibit == 2))
|| (!inhibit_vertical
@ -907,7 +909,13 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
f->new_size_p = false;
}
if (FRAME_TERMINAL (f)->set_window_size_hook)
if (size_and_position)
/* The caller must have set the new position and gravity and
made sure that set_window_size_and_position_hook has been
defined. */
FRAME_TERMINAL (f)->set_window_size_and_position_hook
(f, new_native_width, new_native_height);
else if (FRAME_TERMINAL (f)->set_window_size_hook)
FRAME_TERMINAL (f)->set_window_size_hook
(f, 0, new_native_width, new_native_height);
f->resized_p = true;
@ -4545,6 +4553,69 @@ bottom edge of FRAME's display. */)
return Qt;
}
DEFUN ("set-frame-size-and-position-pixelwise", Fset_frame_size_and_position_pixelwise,
Sset_frame_size_and_position_pixelwise, 5, 6, 0,
doc: /* Set FRAME's size to WIDTH and HEIGHT and its position to (X, Y).
FRAME must be a live frame and defaults to the selected one.
WIDTH and HEIGHT must be positive integers and specify the new pixel
width and height of FRAME's text area in pixels. If WIDTH or HEIGHT do
not secify a value that is a multiple of FRAME's character sizes, you
may have to set `frame-resize-pixelwise' to a non-nil value in order to
get the exact size in pixels.
X and Y specify the coordinates of the left and top edge of FRAME's
outer frame in pixels relative to an origin (0, 0) of FRAME's display or
parent frame. Negative values mean the top or left edge may be outside
the display or parent frame.
GRAVITY specifies the new gravity of FRAME and must be a value in the
range 0..10. It defaults to 1.
This function uses any existing backend of the toolkit to resize and
move FRAME in one compound step. If the backend does not provide such a
function, it calls `set-frame-size' followed by `set-frame-position'
instead. See 'set-frame-size-and-position'. */)
(Lisp_Object frame, Lisp_Object width, Lisp_Object height,
Lisp_Object x, Lisp_Object y, Lisp_Object gravity)
{
struct frame *f = decode_live_frame (frame);
if (NILP (gravity))
f->win_gravity = 1;
else
f->win_gravity = check_integer_range (gravity, 0, 10);
if (FRAME_WINDOW_P (f)
&& FRAME_TERMINAL (f)->set_window_size_and_position_hook)
{
int text_width = check_integer_range (width, 1, INT_MAX);
int text_height = check_integer_range (height, 1, INT_MAX);
f->left_pos = check_integer_range (x, INT_MIN, INT_MAX);
f->top_pos = check_integer_range (y, INT_MIN, INT_MAX);
adjust_frame_size (f, text_width, text_height, 1, false,
Qsize_and_position);
}
else
{
Fset_frame_size (frame, width, height, Qt);
int left_pos = check_integer_range (x, INT_MIN, INT_MAX);
int top_pos = check_integer_range (y, INT_MIN, INT_MAX);
Lisp_Object left
= Fcons (Qleft, left_pos < 0 ? list2 (Qplus, x) : x);
Lisp_Object top
= Fcons (Qtop, top_pos < 0 ? list2 (Qplus, y) : y);
Fmodify_frame_parameters (frame, list2 (left, top));
}
return Qnil;
}
DEFUN ("frame-window-state-change", Fframe_window_state_change,
Sframe_window_state_change, 0, 1, 0,
doc: /* Return t if FRAME's window state change flag is set, nil otherwise.
@ -7113,6 +7184,7 @@ syms_of_frame (void)
DEFSYM (Qmin_height, "min-height");
DEFSYM (Qmouse_wheel_frame, "mouse-wheel-frame");
DEFSYM (Qkeep_ratio, "keep-ratio");
DEFSYM (Qsize_and_position, "size-and-position");
DEFSYM (Qwidth_only, "width-only");
DEFSYM (Qheight_only, "height-only");
DEFSYM (Qleft_only, "left-only");
@ -7593,6 +7665,7 @@ The default is \\+`inhibit' in NS builds and nil everywhere else. */);
defsubr (&Sset_frame_size);
defsubr (&Sframe_position);
defsubr (&Sset_frame_position);
defsubr (&Sset_frame_size_and_position_pixelwise);
defsubr (&Sframe_pointer_visible_p);
defsubr (&Smouse_position_in_root_frame);
defsubr (&Sframe__set_was_invisible);

View file

@ -1384,6 +1384,67 @@ xg_height_or_width_changed (struct frame *f)
}
#endif
/** Move and resize the outer window of frame F. WIDTH and HEIGHT are
the new native pixel sizes of F. */
void
xg_frame_set_size_and_position (struct frame *f, int width, int height)
{
int outer_height
= height + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
int outer_width = width + FRAME_TOOLBAR_WIDTH (f);
int scale = xg_get_scale (f);
int x = f->left_pos;
int y = f->top_pos;
GdkWindow *gwin = NULL;
int flags = 0;
if (FRAME_GTK_OUTER_WIDGET (f))
gwin = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f));
else
gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
outer_height /= scale;
outer_width /= scale;
/* Full force ahead. For top-level frames the gravity will get reset
to NorthWestGravity anyway. */
flags |= USSize;
if (f->win_gravity == 3 || f->win_gravity == 9)
flags |= XNegative;
if (f->win_gravity == 6 || f->win_gravity == 9)
flags |= YNegative;
flags |= USPosition;
xg_wm_set_size_hint (f, flags, true);
#ifndef HAVE_PGTK
gdk_window_move_resize (gwin, x, y, outer_width, outer_height);
#else
if (FRAME_GTK_OUTER_WIDGET (f))
gdk_window_move_resize (gwin, x, y, outer_width, outer_height);
else
gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
outer_width, outer_height);
#endif
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
if (FRAME_VISIBLE_P (f))
{
/* Must call this to flush out events */
(void)gtk_events_pending ();
gdk_flush ();
#ifndef HAVE_PGTK
x_wait_for_event (f, ConfigureNotify);
#endif
}
else
adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
5, 0, Qxg_frame_set_char_size);
}
#ifndef HAVE_PGTK
/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
Must be done like this, because GtkWidget:s can have "hidden"
@ -2041,28 +2102,33 @@ xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
/* These currently have a one to one mapping with the X values, but I
don't think we should rely on that. */
hint_flags |= GDK_HINT_WIN_GRAVITY;
size_hints.win_gravity = 0;
if (win_gravity == NorthWestGravity)
if (FRAME_PARENT_FRAME (f))
{
hint_flags |= GDK_HINT_WIN_GRAVITY;
size_hints.win_gravity = 0;
if (win_gravity == NorthWestGravity)
size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
else if (win_gravity == NorthGravity)
size_hints.win_gravity = GDK_GRAVITY_NORTH;
else if (win_gravity == NorthEastGravity)
size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
else if (win_gravity == WestGravity)
size_hints.win_gravity = GDK_GRAVITY_WEST;
else if (win_gravity == CenterGravity)
size_hints.win_gravity = GDK_GRAVITY_CENTER;
else if (win_gravity == EastGravity)
size_hints.win_gravity = GDK_GRAVITY_EAST;
else if (win_gravity == SouthWestGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
else if (win_gravity == SouthGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH;
else if (win_gravity == SouthEastGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
else if (win_gravity == StaticGravity)
size_hints.win_gravity = GDK_GRAVITY_STATIC;
}
else
size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
else if (win_gravity == NorthGravity)
size_hints.win_gravity = GDK_GRAVITY_NORTH;
else if (win_gravity == NorthEastGravity)
size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
else if (win_gravity == WestGravity)
size_hints.win_gravity = GDK_GRAVITY_WEST;
else if (win_gravity == CenterGravity)
size_hints.win_gravity = GDK_GRAVITY_CENTER;
else if (win_gravity == EastGravity)
size_hints.win_gravity = GDK_GRAVITY_EAST;
else if (win_gravity == SouthWestGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
else if (win_gravity == SouthGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH;
else if (win_gravity == SouthEastGravity)
size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
else if (win_gravity == StaticGravity)
size_hints.win_gravity = GDK_GRAVITY_STATIC;
if (flags & PPosition)
hint_flags |= GDK_HINT_POS;

View file

@ -163,6 +163,8 @@ extern void xg_frame_resized (struct frame *f,
int pixelwidth,
int pixelheight);
extern void xg_frame_set_char_size (struct frame *f, int width, int height);
extern void xg_frame_set_size_and_position (struct frame *f, int width,
int height);
extern GtkWidget * xg_win_to_widget (Display *dpy, Window wdesc);
extern int xg_get_scale (struct frame *f);

View file

@ -684,6 +684,11 @@ struct terminal
void (*set_window_size_hook) (struct frame *f, bool change_gravity,
int width, int height);
/* This hook is called to change the size and position of frame F's
native (underlying) window. */
void (*set_window_size_and_position_hook) (struct frame *f, int width,
int height);
/* CHANGE_GRAVITY is 1 when calling from Fset_frame_position,
to really change the position, and 0 when calling from
*_make_frame_visible (in that case, XOFF and YOFF are the current

View file

@ -7237,6 +7237,85 @@ w32_set_window_size (struct frame *f, bool change_gravity,
do_pending_window_change (false);
}
/* Change the size of frame F's Windows window to WIDTH and HEIGHT
pixels and its position to those stored in f->left_pos and
f->top_pos. */
static void
w32_set_window_size_and_position (struct frame *f, int width, int height)
{
RECT rect;
MENUBARINFO info;
int menu_bar_height;
block_input ();
/* Get the height of the menu bar here. It's used below to detect
whether the menu bar is wrapped. It's also used to specify the
third argument for AdjustWindowRect. See bug#22105. */
info.cbSize = sizeof (info);
info.rcBar.top = info.rcBar.bottom = 0;
GetMenuBarInfo (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &info);
menu_bar_height = info.rcBar.bottom - info.rcBar.top;
if (w32_add_wrapped_menu_bar_lines)
{
/* When the menu bar wraps sending a SetWindowPos shrinks the
height of the frame then the wrapped menu bar lines are not
accounted for (Bug#15174 and Bug#18720). Here we add these
extra lines to the frame height. */
int default_menu_bar_height;
/* Why is (apparently) SM_CYMENUSIZE needed here instead of
SM_CYMENU ?? */
default_menu_bar_height = GetSystemMetrics (SM_CYMENUSIZE);
if ((default_menu_bar_height > 0)
&& (menu_bar_height > default_menu_bar_height)
&& ((menu_bar_height % default_menu_bar_height) == 0))
height = height + menu_bar_height - default_menu_bar_height;
}
f->win_gravity = NorthWestGravity;
w32_wm_set_size_hint (f, (long) 0, false);
rect.left = f->left_pos;
rect.top = f->top_pos;
rect.right = rect.left + width;
rect.bottom = rect.top + height;
AdjustWindowRect (&rect, f->output_data.w32->dwStyle, menu_bar_height > 0);
if (!FRAME_PARENT_FRAME (f))
my_set_window_pos (FRAME_W32_WINDOW (f), NULL,
f->left_pos, f->top_pos,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOACTIVATE);
else
my_set_window_pos (FRAME_W32_WINDOW (f), HWND_TOP,
f->left_pos, f->top_pos,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE);
change_frame_size (f, width, height, false, true, false);
SET_FRAME_GARBAGED (f);
/* If cursor was outside the new size, mark it as off. */
mark_window_cursors_off (XWINDOW (f->root_window));
/* Clear out any recollection of where the mouse highlighting was,
since it might be in a place that's outside the new frame size.
Actually checking whether it is outside is a pain in the neck,
so don't try--just let the highlighting be done afresh with new
size. */
cancel_mouse_face (f);
unblock_input ();
do_pending_window_change (false);
}
/* Mouse warping. */
@ -7891,6 +7970,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo)
terminal->fullscreen_hook = w32fullscreen_hook;
terminal->iconify_frame_hook = w32_iconify_frame;
terminal->set_window_size_hook = w32_set_window_size;
terminal->set_window_size_and_position_hook = w32_set_window_size_and_position;
terminal->set_frame_offset_hook = w32_set_offset;
terminal->set_frame_alpha_hook = w32_set_frame_alpha;
terminal->set_new_font_hook = w32_new_font;

View file

@ -28563,6 +28563,59 @@ x_set_window_size (struct frame *f, bool change_gravity,
do_pending_window_change (false);
}
static void
x_set_window_size_and_position_1 (struct frame *f, int width, int height)
{
int x = f->left_pos;
int y = f->top_pos;
x_wm_set_size_hint (f, 0, false);
XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
x, y, width, height + FRAME_MENUBAR_HEIGHT (f));
SET_FRAME_GARBAGED (f);
if (FRAME_VISIBLE_P (f))
x_wait_for_event (f, ConfigureNotify);
else
/* Call adjust_frame_size right away as with GTK. It might be
tempting to clear out f->new_width and f->new_height here. */
adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
5, 0, Qx_set_window_size_1);
}
void
x_set_window_size_and_position (struct frame *f, int width, int height)
{
block_input ();
#ifdef USE_GTK
if (FRAME_GTK_WIDGET (f))
xg_frame_set_size_and_position (f, width, height);
else
x_set_window_size_and_position_1 (f, width, height);
#else /* not USE_GTK */
x_set_window_size_and_position_1 (f, width, height);
#endif /* USE_GTK */
x_clear_under_internal_border (f);
/* If cursor was outside the new size, mark it as off. */
mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
/* Clear out any recollection of where the mouse highlighting was,
since it might be in a place that's outside the new frame size.
Actually checking whether it is outside is a pain in the neck,
so don't try--just let the highlighting be done afresh with new size. */
cancel_mouse_face (f);
unblock_input ();
do_pending_window_change (false);
}
/* Move the mouse to position pixel PIX_X, PIX_Y relative to frame F. */
void
@ -32148,6 +32201,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
terminal->fullscreen_hook = XTfullscreen_hook;
terminal->iconify_frame_hook = x_iconify_frame;
terminal->set_window_size_hook = x_set_window_size;
terminal->set_window_size_and_position_hook = x_set_window_size_and_position;
terminal->set_frame_offset_hook = x_set_offset;
terminal->set_frame_alpha_hook = x_set_frame_alpha;
terminal->set_new_font_hook = x_new_font;

View file

@ -1766,6 +1766,7 @@ extern void x_ignore_errors_for_next_request (struct x_display_info *,
extern void x_stop_ignoring_errors (struct x_display_info *);
extern void x_clear_errors (Display *);
extern void x_set_window_size (struct frame *, bool, int, int);
extern void x_set_window_size_and_position (struct frame *, int, int);
extern void x_set_last_user_time_from_lisp (struct x_display_info *, Time);
extern void x_make_frame_visible (struct frame *);
extern void x_make_frame_invisible (struct frame *);