1 /* The emacs frame widget.
2 Copyright (C) 1992-1993, 2000-2021 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19 /* Emacs 19 face widget ported by Fred Pierresteguy */
20
21 /* This file has been censored by the Communications Decency Act.
22 That law was passed under the guise of a ban on pornography, but
23 it bans far more than that. This file did not contain pornography,
24 but it was censored nonetheless. */
25
26 #include <config.h>
27 #include "widget.h"
28
29 #include <stdlib.h>
30
31 #include "lisp.h"
32 #include "sysstdio.h"
33 #include "xterm.h"
34 #include "frame.h"
35
36 #include <X11/StringDefs.h>
37 #include <X11/IntrinsicP.h>
38 #include <X11/cursorfont.h>
39 #include "widgetprv.h"
40 #include <X11/ObjectP.h>
41 #include <X11/Shell.h>
42 #include <X11/ShellP.h>
43 #include "../lwlib/lwlib.h"
44
45 static void EmacsFrameInitialize (Widget request, Widget new, ArgList dum1, Cardinal *dum2);
46 static void EmacsFrameDestroy (Widget widget);
47 static void EmacsFrameRealize (Widget widget, XtValueMask *mask, XSetWindowAttributes *attrs);
48 static void EmacsFrameResize (Widget widget);
49 static XtGeometryResult EmacsFrameQueryGeometry (Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *result);
50
51
52 #define offset(field) offsetof (EmacsFrameRec, emacs_frame.field)
53
54 static XtResource resources[] = {
55 {(char *) XtNgeometry, (char *) XtCGeometry, XtRString, sizeof (String),
56 offset (geometry), XtRString, (XtPointer) 0},
57 {XtNiconic, XtCIconic, XtRBoolean, sizeof (Boolean),
58 offset (iconic), XtRImmediate, (XtPointer) False},
59
60 {(char *) XtNemacsFrame, (char *) XtCEmacsFrame,
61 XtRPointer, sizeof (XtPointer),
62 offset (frame), XtRImmediate, 0},
63
64 {(char *) XtNminibuffer, (char *) XtCMinibuffer, XtRInt, sizeof (int),
65 offset (minibuffer), XtRImmediate, (XtPointer)0},
66 {(char *) XtNunsplittable, (char *) XtCUnsplittable,
67 XtRBoolean, sizeof (Boolean),
68 offset (unsplittable), XtRImmediate, (XtPointer)0},
69 {(char *) XtNinternalBorderWidth, (char *) XtCInternalBorderWidth,
70 XtRInt, sizeof (int),
71 offset (internal_border_width), XtRImmediate, (XtPointer)4},
72 {(char *) XtNinterline, (char *) XtCInterline, XtRInt, sizeof (int),
73 offset (interline), XtRImmediate, (XtPointer)0},
74 {(char *) XtNforeground, (char *) XtCForeground, XtRPixel, sizeof (Pixel),
75 offset (foreground_pixel), XtRString, (char *) "XtDefaultForeground"},
76 {(char *) XtNcursorColor, (char *) XtCForeground, XtRPixel, sizeof (Pixel),
77 offset (cursor_color), XtRString, (char *) "XtDefaultForeground"},
78 {(char *) XtNbarCursor, (char *) XtCBarCursor, XtRBoolean, sizeof (Boolean),
79 offset (bar_cursor), XtRImmediate, (XtPointer)0},
80 {(char *) XtNvisualBell, (char *) XtCVisualBell, XtRBoolean, sizeof (Boolean),
81 offset (visual_bell), XtRImmediate, (XtPointer)0},
82 {(char *) XtNbellVolume, (char *) XtCBellVolume, XtRInt, sizeof (int),
83 offset (bell_volume), XtRImmediate, (XtPointer)0},
84 };
85
86 #undef offset
87
88 /*
89 static XtActionsRec
90 emacsFrameActionsTable [] = {
91 {"keypress", key_press},
92 {"focus_in", emacs_frame_focus_handler},
93 {"focus_out", emacs_frame_focus_handler},
94 };
95
96 static char
97 emacsFrameTranslations [] = "\
98 <KeyPress>: keypress()\n\
99 <FocusIn>: focus_in()\n\
100 <FocusOut>: focus_out()\n\
101 ";
102 */
103
104 static EmacsFrameClassRec emacsFrameClassRec = {
105 { /* core fields */
106 /* superclass */ 0, /* filled in by emacsFrameClass */
107 /* class_name */ (char *) "EmacsFrame",
108 /* widget_size */ sizeof (EmacsFrameRec),
109 /* class_initialize */ 0,
110 /* class_part_initialize */ 0,
111 /* class_inited */ FALSE,
112 /* initialize */ EmacsFrameInitialize,
113 /* initialize_hook */ 0,
114 /* realize */ EmacsFrameRealize,
115 /* actions */ 0, /*emacsFrameActionsTable*/
116 /* num_actions */ 0, /*XtNumber (emacsFrameActionsTable)*/
117 /* resources */ resources,
118 /* resource_count */ XtNumber (resources),
119 /* xrm_class */ NULLQUARK,
120 /* compress_motion */ TRUE,
121 /* compress_exposure */ TRUE,
122 /* compress_enterleave */ TRUE,
123 /* visible_interest */ FALSE,
124 /* destroy */ EmacsFrameDestroy,
125 /* resize */ EmacsFrameResize,
126 /* expose */ XtInheritExpose,
127
128 /* Emacs never does XtSetvalues on this widget, so we have no code
129 for it. */
130 /* set_values */ 0, /* Not supported */
131 /* set_values_hook */ 0,
132 /* set_values_almost */ XtInheritSetValuesAlmost,
133 /* get_values_hook */ 0,
134 /* accept_focus */ XtInheritAcceptFocus,
135 /* version */ XtVersion,
136 /* callback_private */ 0,
137 /* tm_table */ 0, /*emacsFrameTranslations*/
138 /* query_geometry */ EmacsFrameQueryGeometry,
139 /* display_accelerator */ XtInheritDisplayAccelerator,
140 /* extension */ 0
141 }
142 };
143
144 WidgetClass
emacsFrameClass(void)145 emacsFrameClass (void)
146 {
147 /* Set the superclass here rather than relying on static
148 initialization, to work around an unexelf.c bug on x86 platforms
149 that use the GNU Gold linker (Bug#27248). */
150 emacsFrameClassRec.core_class.superclass = &widgetClassRec;
151
152 return (WidgetClass) &emacsFrameClassRec;
153 }
154
155 static void
get_default_char_pixel_size(EmacsFrame ew,int * pixel_width,int * pixel_height)156 get_default_char_pixel_size (EmacsFrame ew, int *pixel_width, int *pixel_height)
157 {
158 struct frame *f = ew->emacs_frame.frame;
159 *pixel_width = FRAME_COLUMN_WIDTH (f);
160 *pixel_height = FRAME_LINE_HEIGHT (f);
161 }
162
163 static void
pixel_to_char_size(EmacsFrame ew,Dimension pixel_width,Dimension pixel_height,int * char_width,int * char_height)164 pixel_to_char_size (EmacsFrame ew, Dimension pixel_width, Dimension pixel_height, int *char_width, int *char_height)
165 {
166 struct frame *f = ew->emacs_frame.frame;
167 *char_width = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, (int) pixel_width);
168 *char_height = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (int) pixel_height);
169 }
170
171 static void
char_to_pixel_size(EmacsFrame ew,int char_width,int char_height,Dimension * pixel_width,Dimension * pixel_height)172 char_to_pixel_size (EmacsFrame ew, int char_width, int char_height, Dimension *pixel_width, Dimension *pixel_height)
173 {
174 struct frame *f = ew->emacs_frame.frame;
175 *pixel_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, char_width);
176 *pixel_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, char_height);
177 }
178
179 static void
round_size_to_char(EmacsFrame ew,Dimension in_width,Dimension in_height,Dimension * out_width,Dimension * out_height)180 round_size_to_char (EmacsFrame ew, Dimension in_width, Dimension in_height, Dimension *out_width, Dimension *out_height)
181 {
182 int char_width;
183 int char_height;
184 pixel_to_char_size (ew, in_width, in_height, &char_width, &char_height);
185 char_to_pixel_size (ew, char_width, char_height, out_width, out_height);
186 }
187
188 static Widget
get_wm_shell(Widget w)189 get_wm_shell (Widget w)
190 {
191 Widget wmshell;
192
193 for (wmshell = XtParent (w);
194 wmshell && !XtIsWMShell (wmshell);
195 wmshell = XtParent (wmshell));
196
197 return wmshell;
198 }
199
200 #if 0 /* Currently not used. */
201
202 static void
203 mark_shell_size_user_specified (Widget wmshell)
204 {
205 if (! XtIsWMShell (wmshell)) emacs_abort ();
206 /* This is kind of sleazy, but I can't see how else to tell it to make it
207 mark the WM_SIZE_HINTS size as user specified when appropriate. */
208 ((WMShellWidget) wmshell)->wm.size_hints.flags |= USSize;
209 }
210
211 #endif
212
213
214 static void
set_frame_size(EmacsFrame ew)215 set_frame_size (EmacsFrame ew)
216 {
217 /* The widget hierarchy is
218
219 argv[0] emacsShell pane Frame-NAME
220 ApplicationShell EmacsShell Paned EmacsFrame
221
222 We accept geometry specs in this order:
223
224 *Frame-NAME.geometry
225 *EmacsFrame.geometry
226 Emacs.geometry
227
228 Other possibilities for widget hierarchies might be
229
230 argv[0] frame pane Frame-NAME
231 ApplicationShell EmacsShell Paned EmacsFrame
232 or
233 argv[0] Frame-NAME pane Frame-NAME
234 ApplicationShell EmacsShell Paned EmacsFrame
235 or
236 argv[0] Frame-NAME pane emacsTextPane
237 ApplicationShell EmacsFrame Paned EmacsTextPane
238
239 With the current setup, the text-display-area is the part which is
240 an emacs "frame", since that's the only part managed by emacs proper
241 (the menubar and the parent of the menubar and all that sort of thing
242 are managed by lwlib.)
243
244 The EmacsShell widget is simply a replacement for the Shell widget
245 which is able to deal with using an externally-supplied window instead
246 of always creating its own. It is not actually emacs specific, and
247 should possibly have class "Shell" instead of "EmacsShell" to simplify
248 the resources.
249
250 */
251
252 struct frame *f = ew->emacs_frame.frame;
253
254 ew->core.width = FRAME_PIXEL_WIDTH (f);
255 ew->core.height = FRAME_PIXEL_HEIGHT (f);
256
257 if (CONSP (frame_size_history))
258 frame_size_history_plain
259 (f, build_string ("set_frame_size"));
260 }
261
262 static void
update_wm_hints(EmacsFrame ew)263 update_wm_hints (EmacsFrame ew)
264 {
265 Widget wmshell = get_wm_shell ((Widget) ew);
266 int cw;
267 int ch;
268 Dimension rounded_width;
269 Dimension rounded_height;
270 int char_width;
271 int char_height;
272 int base_width;
273 int base_height;
274
275 /* This happens when the frame is just created. */
276 if (! wmshell) return;
277
278 pixel_to_char_size (ew, ew->core.width, ew->core.height,
279 &char_width, &char_height);
280 char_to_pixel_size (ew, char_width, char_height,
281 &rounded_width, &rounded_height);
282 get_default_char_pixel_size (ew, &cw, &ch);
283
284 base_width = (wmshell->core.width - ew->core.width
285 + (rounded_width - (char_width * cw)));
286 base_height = (wmshell->core.height - ew->core.height
287 + (rounded_height - (char_height * ch)));
288
289 /* This is kind of sleazy, but I can't see how else to tell it to
290 make it mark the WM_SIZE_HINTS size as user specified.
291 */
292 /* ((WMShellWidget) wmshell)->wm.size_hints.flags |= USSize;*/
293
294 XtVaSetValues (wmshell,
295 XtNbaseWidth, (XtArgVal) base_width,
296 XtNbaseHeight, (XtArgVal) base_height,
297 XtNwidthInc, (XtArgVal) (frame_resize_pixelwise ? 1 : cw),
298 XtNheightInc, (XtArgVal) (frame_resize_pixelwise ? 1 : ch),
299 XtNminWidth, (XtArgVal) base_width,
300 XtNminHeight, (XtArgVal) base_height,
301 NULL);
302 }
303
304 void
widget_update_wm_size_hints(Widget widget)305 widget_update_wm_size_hints (Widget widget)
306 {
307 EmacsFrame ew = (EmacsFrame) widget;
308 update_wm_hints (ew);
309 }
310
311 static void
update_various_frame_slots(EmacsFrame ew)312 update_various_frame_slots (EmacsFrame ew)
313 {
314 struct frame *f = ew->emacs_frame.frame;
315
316 f->internal_border_width = ew->emacs_frame.internal_border_width;
317 }
318
319 static void
update_from_various_frame_slots(EmacsFrame ew)320 update_from_various_frame_slots (EmacsFrame ew)
321 {
322 struct frame *f = ew->emacs_frame.frame;
323 struct x_output *x = f->output_data.x;
324
325 ew->core.height = FRAME_PIXEL_HEIGHT (f) - x->menubar_height;
326 ew->core.width = FRAME_PIXEL_WIDTH (f);
327 ew->core.background_pixel = FRAME_BACKGROUND_PIXEL (f);
328 ew->emacs_frame.internal_border_width = f->internal_border_width;
329 ew->emacs_frame.foreground_pixel = FRAME_FOREGROUND_PIXEL (f);
330 ew->emacs_frame.cursor_color = x->cursor_pixel;
331 ew->core.border_pixel = x->border_pixel;
332
333 if (CONSP (frame_size_history))
334 frame_size_history_extra
335 (f, build_string ("update_from_various_frame_slots"),
336 FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
337 ew->core.width, ew->core.height,
338 f->new_width, f->new_height);
339 }
340
341 static void
EmacsFrameInitialize(Widget request,Widget new,ArgList dum1,Cardinal * dum2)342 EmacsFrameInitialize (Widget request, Widget new, ArgList dum1, Cardinal *dum2)
343 {
344 EmacsFrame ew = (EmacsFrame) new;
345
346 if (!ew->emacs_frame.frame)
347 {
348 fputs ("can't create an emacs frame widget without a frame\n", stderr);
349 exit (1);
350 }
351
352 update_from_various_frame_slots (ew);
353 set_frame_size (ew);
354 }
355
356 static void
resize_cb(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)357 resize_cb (Widget widget,
358 XtPointer closure,
359 XEvent *event,
360 Boolean *continue_to_dispatch)
361 {
362 EmacsFrameResize (widget);
363 }
364
365
366 static void
EmacsFrameRealize(Widget widget,XtValueMask * mask,XSetWindowAttributes * attrs)367 EmacsFrameRealize (Widget widget, XtValueMask *mask, XSetWindowAttributes *attrs)
368 {
369 EmacsFrame ew = (EmacsFrame) widget;
370 struct frame *f = ew->emacs_frame.frame;
371
372 /* This used to contain SubstructureRedirectMask, but this turns out
373 to be a problem with XIM on Solaris, and events from that mask
374 don't seem to be used. Let's check that. */
375 attrs->event_mask = (STANDARD_EVENT_SET
376 | PropertyChangeMask
377 | SubstructureNotifyMask);
378 *mask |= CWEventMask;
379 XtCreateWindow (widget, InputOutput, (Visual *) CopyFromParent, *mask,
380 attrs);
381 /* Some ConfigureNotify events does not end up in EmacsFrameResize so
382 make sure we get them all. Seen with xfcwm4 for example. */
383 XtAddRawEventHandler (widget, StructureNotifyMask, False, resize_cb, NULL);
384
385 if (CONSP (frame_size_history))
386 frame_size_history_plain
387 (f, build_string ("EmacsFrameRealize"));
388
389 update_wm_hints (ew);
390 }
391
392 static void
EmacsFrameDestroy(Widget widget)393 EmacsFrameDestroy (Widget widget)
394 {
395 /* All GCs are now freed in x_free_frame_resources. */
396 }
397
398 static void
EmacsFrameResize(Widget widget)399 EmacsFrameResize (Widget widget)
400 {
401 EmacsFrame ew = (EmacsFrame) widget;
402 struct frame *f = ew->emacs_frame.frame;
403
404 if (CONSP (frame_size_history))
405 frame_size_history_extra
406 (f, build_string ("EmacsFrameResize"),
407 FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
408 ew->core.width, ew->core.height,
409 f->new_width, f->new_height);
410
411 change_frame_size (f, ew->core.width, ew->core.height, false, true, false);
412
413 update_wm_hints (ew);
414 update_various_frame_slots (ew);
415
416 cancel_mouse_face (f);
417 }
418
419 static XtGeometryResult
EmacsFrameQueryGeometry(Widget widget,XtWidgetGeometry * request,XtWidgetGeometry * result)420 EmacsFrameQueryGeometry (Widget widget, XtWidgetGeometry *request,
421 XtWidgetGeometry *result)
422 {
423 int mask = request->request_mode;
424
425 if (mask & (CWWidth | CWHeight) && !frame_resize_pixelwise)
426 {
427 EmacsFrame ew = (EmacsFrame) widget;
428 Dimension ok_width, ok_height;
429
430 round_size_to_char (ew,
431 mask & CWWidth ? request->width : ew->core.width,
432 mask & CWHeight ? request->height : ew->core.height,
433 &ok_width, &ok_height);
434 if ((mask & CWWidth) && (ok_width != request->width))
435 {
436 result->request_mode |= CWWidth;
437 result->width = ok_width;
438 }
439 if ((mask & CWHeight) && (ok_height != request->height))
440 {
441 result->request_mode |= CWHeight;
442 result->height = ok_height;
443 }
444 }
445 return result->request_mode ? XtGeometryAlmost : XtGeometryYes;
446 }
447
448 /* Special entry points */
449 void
EmacsFrameSetCharSize(Widget widget,int columns,int rows)450 EmacsFrameSetCharSize (Widget widget, int columns, int rows)
451 {
452 EmacsFrame ew = (EmacsFrame) widget;
453 struct frame *f = ew->emacs_frame.frame;
454
455 if (CONSP (frame_size_history))
456 frame_size_history_extra
457 (f, build_string ("EmacsFrameSetCharSize"),
458 FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
459 columns, rows,
460 f->new_width, f->new_height);
461
462 if (!frame_inhibit_resize (f, 0, Qfont)
463 && !frame_inhibit_resize (f, 1, Qfont))
464 x_set_window_size (f, 0, columns * FRAME_COLUMN_WIDTH (f),
465 rows * FRAME_LINE_HEIGHT (f));
466 }
467
468
469 void
widget_store_internal_border(Widget widget)470 widget_store_internal_border (Widget widget)
471 {
472 EmacsFrame ew = (EmacsFrame) widget;
473 struct frame *f = ew->emacs_frame.frame;
474
475 ew->emacs_frame.internal_border_width = f->internal_border_width;
476 }
477