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