Mercurial > hg > xemacs-beta
comparison src/redisplay-x.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 9ee227acff29 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 /* X output and frame manipulation routines. | |
2 Copyright (C) 1994, 1995 Board of Trustees, University of Illinois. | |
3 Copyright (C) 1994 Lucid, Inc. | |
4 Copyright (C) 1995 Sun Microsystems, Inc. | |
5 | |
6 This file is part of XEmacs. | |
7 | |
8 XEmacs is free software; you can redistribute it and/or modify it | |
9 under the terms of the GNU General Public License as published by the | |
10 Free Software Foundation; either version 2, or (at your option) any | |
11 later version. | |
12 | |
13 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with XEmacs; see the file COPYING. If not, write to | |
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
21 Boston, MA 02111-1307, USA. */ | |
22 | |
23 /* Synched up with: Not in FSF. */ | |
24 | |
25 /* Author: Chuck Thompson */ | |
26 | |
27 /* Lots of work done by Ben Wing for Mule */ | |
28 | |
29 #include <config.h> | |
30 #include "lisp.h" | |
31 | |
32 #include "console-x.h" | |
33 #include "EmacsFrame.h" | |
34 #include "EmacsFrameP.h" | |
35 #include "xgccache.h" | |
36 #include "glyphs-x.h" | |
37 #include "objects-x.h" | |
38 | |
39 #include "buffer.h" | |
40 #include "debug.h" | |
41 #include "faces.h" | |
42 #include "frame.h" | |
43 #include "redisplay.h" | |
44 #include "sysdep.h" | |
45 #include "window.h" | |
46 #include <X11/bitmaps/gray> | |
47 | |
48 #include "sysproc.h" /* for select() */ | |
49 | |
50 /* X_DIVIDER_LINE_WIDTH is the width of the line drawn in the gutter. | |
51 X_DIVIDER_SPACING is the amount of blank space on each side of the line. | |
52 X_DIVIDER_WIDTH = X_DIVIDER_LINE_WIDTH + 2*X_DIVIDER_SPACING | |
53 */ | |
54 | |
55 /* Number of pixels below each line. */ | |
56 /* #### implement me */ | |
57 int x_interline_space; | |
58 | |
59 #define X_DIVIDER_LINE_WIDTH 3 | |
60 #define X_DIVIDER_SPACING 2 | |
61 #define X_DIVIDER_WIDTH (X_DIVIDER_LINE_WIDTH + 2 * X_DIVIDER_SPACING) | |
62 | |
63 #define EOL_CURSOR_WIDTH 5 | |
64 | |
65 static void x_output_pixmap (struct window *w, struct display_line *dl, | |
66 Lisp_Object image_instance, int xpos, | |
67 int xoffset, | |
68 int start_pixpos, int width, face_index findex, | |
69 int cursor_start, int cursor_width, | |
70 int cursor_height); | |
71 static void x_output_vertical_divider (struct window *w, int clear); | |
72 static void x_output_blank (struct window *w, struct display_line *dl, | |
73 struct rune *rb, int start_pixpos, | |
74 int cursor_start, int cursor_width); | |
75 static void x_output_hline (struct window *w, struct display_line *dl, | |
76 struct rune *rb); | |
77 static void x_redraw_exposed_window (struct window *w, int x, int y, | |
78 int width, int height); | |
79 static void x_redraw_exposed_windows (Lisp_Object window, int x, int y, | |
80 int width, int height); | |
81 static void x_clear_region (Lisp_Object window, face_index findex, int x, | |
82 int y, int width, int height); | |
83 static void x_output_eol_cursor (struct window *w, struct display_line *dl, | |
84 int xpos); | |
85 static void x_clear_frame (struct frame *f); | |
86 static void x_clear_frame_windows (Lisp_Object window); | |
87 static void x_bevel_modeline (struct window *w, struct display_line *dl); | |
88 | |
89 | |
90 /* Note: We do not use the Xmb*() functions and XFontSets. | |
91 Those functions are generally losing for a number of reasons: | |
92 | |
93 1) They only support one locale (e.g. you could display | |
94 Japanese and ASCII text, but not mixed Japanese/Chinese | |
95 text). You could maybe call setlocale() frequently | |
96 to try to deal with this, but that would generally | |
97 fail because an XFontSet is tied to one locale and | |
98 won't have the other character sets in it. | |
99 2) Not all (or even very many) OS's support the useful | |
100 locales. For example, as far as I know SunOS and | |
101 Solaris only support the Japanese locale if you get the | |
102 special Asian-language version of the OS. Yuck yuck | |
103 yuck. Linux doesn't support the Japanese locale at | |
104 all. | |
105 3) The locale support in X only exists in R5, not in R4. | |
106 (Not sure how big of a problem this is: how many | |
107 people are using R4?) | |
108 4) Who knows if the multi-byte text format (which is locale- | |
109 specific) is even the same for the same locale on | |
110 different OS's? It's not even documented anywhere that | |
111 I can find what the multi-byte text format for the | |
112 Japanese locale under SunOS and Solaris is, but I assume | |
113 it's EUC. | |
114 */ | |
115 | |
116 struct textual_run | |
117 { | |
118 Lisp_Object charset; | |
119 unsigned char *ptr; | |
120 int len; | |
121 int dimension; | |
122 }; | |
123 | |
124 /* Separate out the text in DYN into a series of textual runs of a | |
125 particular charset. Also convert the characters as necessary into | |
126 the format needed by XDrawImageString(), XDrawImageString16(), et | |
127 al. (This means converting to one or two byte format, possibly | |
128 tweaking the high bits, and possibly running a CCL program.) You | |
129 must pre-allocate the space used and pass it in. (This is done so | |
130 you can alloca() the space.) You need to allocate (2 * len) bytes | |
131 of TEXT_STORAGE and (len * sizeof (struct textual_run)) bytes of | |
132 RUN_STORAGE, where LEN is the length of the dynarr. | |
133 | |
134 Returns the number of runs actually used. */ | |
135 | |
136 static int | |
137 separate_textual_runs (unsigned char *text_storage, | |
138 struct textual_run *run_storage, | |
139 CONST Emchar *str, Charcount len) | |
140 { | |
141 Lisp_Object prev_charset = Qunbound; /* not Qnil because that is a | |
142 possible valid charset when | |
143 MULE is not defined */ | |
144 int runs_so_far = 0; | |
145 int i; | |
146 | |
147 for (i = 0; i < len; i++) | |
148 { | |
149 Emchar ch = str[i]; | |
150 Lisp_Object charset; | |
151 int byte1, byte2; | |
152 int dimension; | |
153 int graphic; | |
154 | |
155 BREAKUP_CHAR (ch, charset, byte1, byte2); | |
156 dimension = XCHARSET_DIMENSION (charset); | |
157 graphic = XCHARSET_GRAPHIC (charset); | |
158 | |
159 if (!EQ (charset, prev_charset)) | |
160 { | |
161 run_storage[runs_so_far].ptr = text_storage; | |
162 run_storage[runs_so_far].charset = charset; | |
163 run_storage[runs_so_far].dimension = dimension; | |
164 | |
165 if (runs_so_far) | |
166 { | |
167 run_storage[runs_so_far - 1].len = | |
168 text_storage - run_storage[runs_so_far - 1].ptr; | |
169 if (run_storage[runs_so_far - 1].dimension == 2) | |
170 run_storage[runs_so_far - 1].len >>= 1; | |
171 } | |
172 runs_so_far++; | |
173 prev_charset = charset; | |
174 } | |
175 | |
176 if (graphic == 0) | |
177 { | |
178 byte1 &= 0x7F; | |
179 byte2 &= 0x7F; | |
180 } | |
181 else if (graphic == 1) | |
182 { | |
183 byte1 |= 0x80; | |
184 byte2 |= 0x80; | |
185 } | |
186 *text_storage++ = (unsigned char) byte1; | |
187 if (dimension == 2) | |
188 *text_storage++ = (unsigned char) byte2; | |
189 } | |
190 | |
191 if (runs_so_far) | |
192 { | |
193 run_storage[runs_so_far - 1].len = | |
194 text_storage - run_storage[runs_so_far - 1].ptr; | |
195 if (run_storage[runs_so_far - 1].dimension == 2) | |
196 run_storage[runs_so_far - 1].len >>= 1; | |
197 } | |
198 | |
199 return runs_so_far; | |
200 } | |
201 | |
202 /****************************************************************************/ | |
203 /* */ | |
204 /* X output routines */ | |
205 /* */ | |
206 /****************************************************************************/ | |
207 | |
208 static int | |
209 x_text_width_single_run (struct face_cachel *cachel, struct textual_run *run) | |
210 { | |
211 Lisp_Object font_inst = FACE_CACHEL_FONT (cachel, run->charset); | |
212 struct Lisp_Font_Instance *fi = XFONT_INSTANCE (font_inst); | |
213 if (!fi->proportional_p) | |
214 return fi->width * run->len; | |
215 else | |
216 { | |
217 if (run->dimension == 2) | |
218 return XTextWidth16 (FONT_INSTANCE_X_FONT (fi), | |
219 (XChar2b *) run->ptr, run->len); | |
220 else | |
221 return XTextWidth (FONT_INSTANCE_X_FONT (fi), | |
222 (char *) run->ptr, run->len); | |
223 } | |
224 } | |
225 | |
226 /* | |
227 x_text_width | |
228 | |
229 Given a string and a face, return the string's length in pixels when | |
230 displayed in the font associated with the face. | |
231 */ | |
232 | |
233 static int | |
234 x_text_width (struct face_cachel *cachel, CONST Emchar *str, | |
235 Charcount len) | |
236 { | |
237 int width_so_far = 0; | |
238 unsigned char *text_storage = (unsigned char *) alloca (2 * len); | |
239 struct textual_run *runs = | |
240 (struct textual_run *) alloca (len * sizeof (struct textual_run)); | |
241 int nruns; | |
242 int i; | |
243 | |
244 nruns = separate_textual_runs (text_storage, runs, str, len); | |
245 | |
246 for (i = 0; i < nruns; i++) | |
247 width_so_far += x_text_width_single_run (cachel, runs + i); | |
248 | |
249 return width_so_far; | |
250 } | |
251 | |
252 | |
253 /***************************************************************************** | |
254 x_divider_width | |
255 | |
256 Return the width of the vertical divider. This is a function because | |
257 divider_width is a device method. | |
258 ****************************************************************************/ | |
259 static int | |
260 x_divider_width (void) | |
261 { | |
262 return X_DIVIDER_WIDTH; | |
263 } | |
264 | |
265 /***************************************************************************** | |
266 x_divider_height | |
267 | |
268 Return the height of the horizontal divider. This is a function because | |
269 divider_height is a device method. | |
270 | |
271 #### If we add etched horizontal divider lines this will have to get | |
272 smarter. | |
273 ****************************************************************************/ | |
274 static int | |
275 x_divider_height (void) | |
276 { | |
277 return 1; | |
278 } | |
279 | |
280 /***************************************************************************** | |
281 x_eol_cursor_width | |
282 | |
283 Return the width of the end-of-line cursor. This is a function | |
284 because eol_cursor_width is a device method. | |
285 ****************************************************************************/ | |
286 static int | |
287 x_eol_cursor_width (void) | |
288 { | |
289 return EOL_CURSOR_WIDTH; | |
290 } | |
291 | |
292 /***************************************************************************** | |
293 x_output_begin | |
294 | |
295 Perform any necessary initialization prior to an update. | |
296 ****************************************************************************/ | |
297 static void | |
298 x_output_begin (struct device *d) | |
299 { | |
300 } | |
301 | |
302 /***************************************************************************** | |
303 x_output_end | |
304 | |
305 Perform any necessary flushing of queues when an update has completed. | |
306 ****************************************************************************/ | |
307 static void | |
308 x_output_end (struct device *d) | |
309 { | |
310 XFlush (DEVICE_X_DISPLAY (d)); | |
311 } | |
312 | |
313 /***************************************************************************** | |
314 x_output_display_block | |
315 | |
316 Given a display line, a block number for that start line, output all | |
317 runes between start and end in the specified display block. | |
318 ****************************************************************************/ | |
319 static void | |
320 x_output_display_block (struct window *w, struct display_line *dl, int block, | |
321 int start, int end, int start_pixpos, int cursor_start, | |
322 int cursor_width, int cursor_height) | |
323 { | |
324 struct frame *f = XFRAME (w->frame); | |
325 emchar_dynarr *buf = Dynarr_new (Emchar); | |
326 Lisp_Object window; | |
327 | |
328 struct display_block *db = Dynarr_atp (dl->display_blocks, block); | |
329 rune_dynarr *rba = db->runes; | |
330 struct rune *rb; | |
331 | |
332 int elt = start; | |
333 face_index findex; | |
334 int xpos, width; | |
335 Lisp_Object charset = Qunbound; /* Qnil is a valid charset when | |
336 MULE is not defined */ | |
337 | |
338 XSETWINDOW (window, w); | |
339 rb = Dynarr_atp (rba, start); | |
340 | |
341 if (!rb) | |
342 { | |
343 /* Nothing to do so don't do anything. */ | |
344 return; | |
345 } | |
346 else | |
347 { | |
348 findex = rb->findex; | |
349 xpos = rb->xpos; | |
350 width = 0; | |
351 if (rb->type == RUNE_CHAR) | |
352 charset = CHAR_CHARSET (rb->object.chr.ch); | |
353 } | |
354 | |
355 if (end < 0) | |
356 end = Dynarr_length (rba); | |
357 Dynarr_reset (buf); | |
358 | |
359 while (elt < end) | |
360 { | |
361 rb = Dynarr_atp (rba, elt); | |
362 | |
363 if (rb->findex == findex && rb->type == RUNE_CHAR | |
364 && rb->object.chr.ch != '\n' && rb->cursor_type != CURSOR_ON | |
365 && EQ (charset, CHAR_CHARSET (rb->object.chr.ch))) | |
366 { | |
367 Dynarr_add (buf, rb->object.chr.ch); | |
368 width += rb->width; | |
369 elt++; | |
370 } | |
371 else | |
372 { | |
373 if (Dynarr_length (buf)) | |
374 { | |
375 x_output_string (w, dl, buf, xpos, 0, start_pixpos, width, | |
376 findex, 0, cursor_start, cursor_width, | |
377 cursor_height); | |
378 xpos = rb->xpos; | |
379 width = 0; | |
380 } | |
381 Dynarr_reset (buf); | |
382 width = 0; | |
383 | |
384 if (rb->type == RUNE_CHAR) | |
385 { | |
386 findex = rb->findex; | |
387 xpos = rb->xpos; | |
388 charset = CHAR_CHARSET (rb->object.chr.ch); | |
389 | |
390 if (rb->cursor_type == CURSOR_ON) | |
391 { | |
392 if (rb->object.chr.ch == '\n') | |
393 { | |
394 x_output_eol_cursor (w, dl, xpos); | |
395 } | |
396 else | |
397 { | |
398 Dynarr_add (buf, rb->object.chr.ch); | |
399 x_output_string (w, dl, buf, xpos, 0, start_pixpos, | |
400 rb->width, findex, 1, | |
401 cursor_start, cursor_width, | |
402 cursor_height); | |
403 Dynarr_reset (buf); | |
404 } | |
405 | |
406 xpos += rb->width; | |
407 elt++; | |
408 } | |
409 else if (rb->object.chr.ch == '\n') | |
410 { | |
411 /* Clear in case a cursor was formerly here. */ | |
412 int height = dl->ascent + dl->descent - dl->clip; | |
413 | |
414 x_clear_region (window, findex, xpos, dl->ypos - dl->ascent, | |
415 rb->width, height); | |
416 elt++; | |
417 } | |
418 } | |
419 else if (rb->type == RUNE_BLANK || rb->type == RUNE_HLINE) | |
420 { | |
421 if (rb->type == RUNE_BLANK) | |
422 x_output_blank (w, dl, rb, start_pixpos, cursor_start, | |
423 cursor_width); | |
424 else | |
425 { | |
426 /* #### Our flagging of when we need to redraw the | |
427 modeline shadows sucks. Since RUNE_HLINE is only used | |
428 by the modeline at the moment it is a good bet | |
429 that if it gets redrawn then we should also | |
430 redraw the shadows. This won't be true forever. | |
431 We borrow the shadow_thickness_changed flag for | |
432 now. */ | |
433 w->shadow_thickness_changed = 1; | |
434 x_output_hline (w, dl, rb); | |
435 } | |
436 | |
437 elt++; | |
438 if (elt < end) | |
439 { | |
440 rb = Dynarr_atp (rba, elt); | |
441 | |
442 findex = rb->findex; | |
443 xpos = rb->xpos; | |
444 } | |
445 } | |
446 else if (rb->type == RUNE_DGLYPH) | |
447 { | |
448 Lisp_Object instance; | |
449 | |
450 XSETWINDOW (window, w); | |
451 instance = glyph_image_instance (rb->object.dglyph.glyph, | |
452 window, ERROR_ME_NOT, 1); | |
453 findex = rb->findex; | |
454 | |
455 if (IMAGE_INSTANCEP (instance)) | |
456 switch (XIMAGE_INSTANCE_TYPE (instance)) | |
457 { | |
458 case IMAGE_TEXT: | |
459 { | |
460 /* #### This is way losing. See the comment in | |
461 add_glyph_rune(). */ | |
462 Lisp_Object string = | |
463 XIMAGE_INSTANCE_TEXT_STRING (instance); | |
464 convert_bufbyte_string_into_emchar_dynarr | |
465 (string_data (XSTRING (string)), | |
466 string_length (XSTRING (string)), | |
467 buf); | |
468 | |
469 x_output_string (w, dl, buf, xpos, | |
470 rb->object.dglyph.xoffset, | |
471 start_pixpos, -1, findex, | |
472 (rb->cursor_type == CURSOR_ON), | |
473 cursor_start, cursor_width, | |
474 cursor_height); | |
475 Dynarr_reset (buf); | |
476 } | |
477 break; | |
478 | |
479 case IMAGE_MONO_PIXMAP: | |
480 case IMAGE_COLOR_PIXMAP: | |
481 x_output_pixmap (w, dl, instance, xpos, | |
482 rb->object.dglyph.xoffset, start_pixpos, | |
483 rb->width, findex, cursor_start, | |
484 cursor_width, cursor_height); | |
485 break; | |
486 | |
487 case IMAGE_POINTER: | |
488 abort (); | |
489 | |
490 case IMAGE_SUBWINDOW: | |
491 /* #### implement me */ | |
492 break; | |
493 | |
494 case IMAGE_NOTHING: | |
495 /* nothing is as nothing does */ | |
496 break; | |
497 | |
498 default: | |
499 abort (); | |
500 } | |
501 | |
502 xpos += rb->width; | |
503 elt++; | |
504 } | |
505 else | |
506 abort (); | |
507 } | |
508 } | |
509 | |
510 if (Dynarr_length (buf)) | |
511 x_output_string (w, dl, buf, xpos, 0, start_pixpos, width, findex, | |
512 0, cursor_start, cursor_width, cursor_height); | |
513 | |
514 /* #### This is really conditionalized well for optimized | |
515 performance. */ | |
516 if (dl->modeline | |
517 && !EQ (Qzero, w->modeline_shadow_thickness) | |
518 && (f->clear | |
519 || f->windows_structure_changed | |
520 || w->shadow_thickness_changed)) | |
521 x_bevel_modeline (w, dl); | |
522 | |
523 Dynarr_free (buf); | |
524 } | |
525 | |
526 /***************************************************************************** | |
527 x_bevel_modeline | |
528 | |
529 Draw a 3d border around the modeline on window W. | |
530 ****************************************************************************/ | |
531 static void | |
532 x_bevel_modeline (struct window *w, struct display_line *dl) | |
533 { | |
534 struct frame *f = XFRAME (w->frame); | |
535 struct device *d = XDEVICE (f->device); | |
536 Display *dpy = DEVICE_X_DISPLAY (d); | |
537 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
538 EmacsFrame ef = (EmacsFrame) FRAME_X_TEXT_WIDGET (f); | |
539 GC top_shadow_gc, bottom_shadow_gc, background_gc; | |
540 Pixel top_shadow_pixel, bottom_shadow_pixel, background_pixel; | |
541 XColor tmp_color; | |
542 Lisp_Object tmp_pixel; | |
543 int x, y, width, height; | |
544 XGCValues gcv; | |
545 unsigned long mask; | |
546 int use_pixmap = 0; | |
547 int flip_gcs = 0; | |
548 int shadow_thickness; | |
549 | |
550 memset (&gcv, ~0, sizeof (XGCValues)); | |
551 | |
552 tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX); | |
553 tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
554 | |
555 /* First, get the GC's. */ | |
556 top_shadow_pixel = tmp_color.pixel; | |
557 bottom_shadow_pixel = tmp_color.pixel; | |
558 background_pixel = tmp_color.pixel; | |
559 | |
560 x_generate_shadow_pixels (f, &top_shadow_pixel, &bottom_shadow_pixel, | |
561 background_pixel, ef->core.background_pixel); | |
562 | |
563 tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX); | |
564 tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
565 gcv.background = tmp_color.pixel; | |
566 gcv.graphics_exposures = False; | |
567 mask = GCForeground | GCBackground | GCGraphicsExposures; | |
568 | |
569 if (top_shadow_pixel == background_pixel || | |
570 bottom_shadow_pixel == background_pixel) | |
571 use_pixmap = 1; | |
572 | |
573 if (use_pixmap) | |
574 { | |
575 if (DEVICE_X_GRAY_PIXMAP (d) == None) | |
576 { | |
577 DEVICE_X_GRAY_PIXMAP (d) = | |
578 XCreatePixmapFromBitmapData (dpy, x_win, (char *) gray_bits, | |
579 gray_width, gray_height, 1, 0, 1); | |
580 } | |
581 | |
582 tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX); | |
583 tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
584 gcv.foreground = tmp_color.pixel; | |
585 gcv.fill_style = FillOpaqueStippled; | |
586 gcv.stipple = DEVICE_X_GRAY_PIXMAP (d); | |
587 top_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, | |
588 (mask | GCStipple | GCFillStyle)); | |
589 | |
590 tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX); | |
591 tmp_color = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
592 bottom_shadow_pixel = tmp_color.pixel; | |
593 | |
594 flip_gcs = (bottom_shadow_pixel == | |
595 WhitePixelOfScreen (DefaultScreenOfDisplay (dpy))); | |
596 } | |
597 else | |
598 { | |
599 gcv.foreground = top_shadow_pixel; | |
600 top_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); | |
601 } | |
602 | |
603 gcv.foreground = bottom_shadow_pixel; | |
604 bottom_shadow_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); | |
605 | |
606 if (use_pixmap && flip_gcs) | |
607 { | |
608 GC tmp_gc = bottom_shadow_gc; | |
609 bottom_shadow_gc = top_shadow_gc; | |
610 top_shadow_gc = tmp_gc; | |
611 } | |
612 | |
613 gcv.foreground = background_pixel; | |
614 background_gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); | |
615 | |
616 if (XINT (w->modeline_shadow_thickness) < 0) | |
617 { | |
618 GC temp; | |
619 | |
620 temp = top_shadow_gc; | |
621 top_shadow_gc = bottom_shadow_gc; | |
622 bottom_shadow_gc = temp; | |
623 } | |
624 | |
625 shadow_thickness = MODELINE_SHADOW_THICKNESS (w); | |
626 | |
627 x = WINDOW_MODELINE_LEFT (w); | |
628 width = WINDOW_MODELINE_RIGHT (w) - x; | |
629 y = dl->ypos - dl->ascent - shadow_thickness; | |
630 height = dl->ascent + dl->descent + 2 * shadow_thickness; | |
631 | |
632 x_output_shadows (f, x, y, width, height, top_shadow_gc, bottom_shadow_gc, | |
633 background_gc, shadow_thickness); | |
634 } | |
635 | |
636 void debug_print (Lisp_Object); /* kludge! */ | |
637 | |
638 /***************************************************************************** | |
639 x_get_gc | |
640 | |
641 Given a number of parameters return a GC with those properties. | |
642 ****************************************************************************/ | |
643 static GC | |
644 x_get_gc (struct device *d, Lisp_Object font, Lisp_Object fg, Lisp_Object bg, | |
645 Lisp_Object bg_pmap, Lisp_Object lwidth) | |
646 { | |
647 XGCValues gcv; | |
648 unsigned long mask; | |
649 | |
650 memset (&gcv, ~0, sizeof (XGCValues)); | |
651 gcv.graphics_exposures = False; | |
652 /* Make absolutely sure that we don't pick up a clipping region in | |
653 the GC returned by this function. */ | |
654 gcv.clip_mask = None; | |
655 gcv.clip_x_origin = 0; | |
656 gcv.clip_y_origin = 0; | |
657 gcv.fill_style = FillSolid; | |
658 mask = GCGraphicsExposures | GCClipMask | GCClipXOrigin | GCClipYOrigin; | |
659 mask |= GCFillStyle; | |
660 | |
661 if (!NILP (font)) | |
662 { | |
663 gcv.font = FONT_INSTANCE_X_FONT (XFONT_INSTANCE (font))->fid; | |
664 mask |= GCFont; | |
665 } | |
666 | |
667 /* evil kludge! */ | |
668 if (!NILP (fg) && !COLOR_INSTANCEP (fg) && !INTP (fg)) | |
669 { | |
670 /* #### I fixed once case where this was getting it. It was a | |
671 bad macro expansion (compiler bug). */ | |
672 fprintf (stderr, "Help! x_get_gc got a bogus fg value! fg = "); | |
673 debug_print (fg); | |
674 fg = Qnil; | |
675 } | |
676 | |
677 if (!NILP (fg)) | |
678 { | |
679 if (COLOR_INSTANCEP (fg)) | |
680 gcv.foreground = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (fg)).pixel; | |
681 else | |
682 gcv.foreground = XINT (fg); | |
683 mask |= GCForeground; | |
684 } | |
685 | |
686 if (!NILP (bg)) | |
687 { | |
688 if (COLOR_INSTANCEP (bg)) | |
689 gcv.background = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (bg)).pixel; | |
690 else | |
691 gcv.background = XINT (bg); | |
692 mask |= GCBackground; | |
693 } | |
694 | |
695 if (IMAGE_INSTANCEP (bg_pmap) | |
696 && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap))) | |
697 { | |
698 if (XIMAGE_INSTANCE_PIXMAP_DEPTH (bg_pmap) == 0) | |
699 { | |
700 gcv.fill_style = FillOpaqueStippled; | |
701 gcv.stipple = XIMAGE_INSTANCE_X_PIXMAP (bg_pmap); | |
702 mask |= (GCStipple | GCFillStyle); | |
703 } | |
704 else | |
705 { | |
706 gcv.fill_style = FillTiled; | |
707 gcv.tile = XIMAGE_INSTANCE_X_PIXMAP (bg_pmap); | |
708 mask |= (GCTile | GCFillStyle); | |
709 } | |
710 } | |
711 | |
712 if (!NILP (lwidth)) | |
713 { | |
714 gcv.line_width = XINT (lwidth); | |
715 mask |= GCLineWidth; | |
716 } | |
717 | |
718 return gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, mask); | |
719 } | |
720 | |
721 /***************************************************************************** | |
722 x_output_string | |
723 | |
724 Given a string and a starting position, output that string in the | |
725 given face. If cursor is true, draw a cursor around the string. | |
726 Correctly handles multiple charsets in the string. | |
727 | |
728 The meaning of the parameters is something like this: | |
729 | |
730 W Window that the text is to be displayed in. | |
731 DL Display line that this text is on. The values in the | |
732 structure are used to determine the vertical position and | |
733 clipping range of the text. | |
734 BUF Dynamic array of Emchars specifying what is actually to be | |
735 drawn. | |
736 XPOS X position in pixels where the text should start being drawn. | |
737 XOFFSET Number of pixels to be chopped off the left side of the | |
738 text. The effect is as if the text were shifted to the | |
739 left this many pixels and clipped at XPOS. | |
740 CLIP_START Clip everything left of this X position. | |
741 WIDTH Clip everything right of XPOS + WIDTH. | |
742 FINDEX Index for the face cache element describing how to display | |
743 the text. | |
744 CURSOR #### I don't understand this. There's something | |
745 strange and overcomplexified with this variable. | |
746 Chuck, explain please? | |
747 CURSOR_START Starting X position of cursor. | |
748 CURSOR_WIDTH Width of cursor in pixels. | |
749 CURSOR_HEIGHT Height of cursor in pixels. | |
750 | |
751 Starting Y position of cursor is the top of the text line. | |
752 The cursor is drawn sometimes whether or not CURSOR is set. ??? | |
753 ****************************************************************************/ | |
754 void | |
755 x_output_string (struct window *w, struct display_line *dl, | |
756 emchar_dynarr *buf, int xpos, int xoffset, int clip_start, | |
757 int width, face_index findex, int cursor, | |
758 int cursor_start, int cursor_width, int cursor_height) | |
759 { | |
760 /* General variables */ | |
761 struct frame *f = XFRAME (w->frame); | |
762 struct device *d = XDEVICE (f->device); | |
763 Lisp_Object device = Qnil; | |
764 Lisp_Object window = Qnil; | |
765 Display *dpy = DEVICE_X_DISPLAY (d); | |
766 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
767 | |
768 int clip_end; | |
769 | |
770 /* Cursor-related variables */ | |
771 int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d)); | |
772 int cursor_clip; | |
773 Lisp_Object bar_cursor_value = symbol_value_in_buffer (Qbar_cursor, | |
774 WINDOW_BUFFER (w)); | |
775 struct face_cachel *cursor_cachel = 0; | |
776 | |
777 /* Text-related variables */ | |
778 Lisp_Object bg_pmap; | |
779 GC bgc, gc; | |
780 int height; | |
781 int len = Dynarr_length (buf); | |
782 unsigned char *text_storage = alloca (2 * len); | |
783 struct textual_run *runs = alloca (len * sizeof (struct textual_run)); | |
784 int nruns; | |
785 int i; | |
786 struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, findex); | |
787 | |
788 XSETDEVICE (device, d); | |
789 XSETWINDOW (window, w); | |
790 | |
791 if (width < 0) | |
792 width = x_text_width (cachel, Dynarr_atp (buf, 0), Dynarr_length (buf)); | |
793 height = dl->ascent + dl->descent - dl->clip; | |
794 | |
795 /* Regularize the variables passed in. */ | |
796 | |
797 if (clip_start < xpos) | |
798 clip_start = xpos; | |
799 clip_end = xpos + width; | |
800 if (clip_start >= clip_end) | |
801 /* It's all clipped out. */ | |
802 return; | |
803 | |
804 xpos -= xoffset; | |
805 | |
806 nruns = separate_textual_runs (text_storage, runs, Dynarr_atp (buf, 0), | |
807 Dynarr_length (buf)); | |
808 | |
809 cursor_clip = (cursor_start >= clip_start && | |
810 cursor_start < clip_end); | |
811 | |
812 /* This cursor code is really a mess. */ | |
813 if (!NILP (w->text_cursor_visible_p) | |
814 && (cursor | |
815 || cursor_clip | |
816 || (cursor_width | |
817 && (cursor_start + cursor_width >= clip_start) | |
818 && !NILP (bar_cursor_value)))) | |
819 { | |
820 /* These have to be in separate statements in order to avoid a | |
821 compiler bug. */ | |
822 face_index sucks = get_builtin_face_cache_index (w, Vtext_cursor_face); | |
823 cursor_cachel = WINDOW_FACE_CACHEL (w, sucks); | |
824 | |
825 /* We have to reset this since any call to WINDOW_FACE_CACHEL | |
826 may cause the cache to resize and any pointers to it to | |
827 become invalid. */ | |
828 cachel = WINDOW_FACE_CACHEL (w, findex); | |
829 } | |
830 | |
831 bg_pmap = cachel->background_pixmap; | |
832 if (!IMAGE_INSTANCEP (bg_pmap) | |
833 || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap))) | |
834 bg_pmap = Qnil; | |
835 | |
836 if ((cursor && focus && NILP (bar_cursor_value) | |
837 && !NILP (w->text_cursor_visible_p)) || NILP (bg_pmap)) | |
838 bgc = 0; | |
839 else | |
840 bgc = x_get_gc (d, Qnil, cachel->foreground, cachel->background, | |
841 bg_pmap, Qnil); | |
842 | |
843 if (bgc) | |
844 XFillRectangle (dpy, x_win, bgc, clip_start, | |
845 dl->ypos - dl->ascent, clip_end - clip_start, | |
846 height); | |
847 | |
848 for (i = 0; i < nruns; i++) | |
849 { | |
850 Lisp_Object font = FACE_CACHEL_FONT (cachel, runs[i].charset); | |
851 struct Lisp_Font_Instance *fi = XFONT_INSTANCE (font); | |
852 int this_width; | |
853 int need_clipping; | |
854 | |
855 if (EQ (font, Vthe_null_font_instance)) | |
856 continue; | |
857 | |
858 this_width = x_text_width_single_run (cachel, runs + i); | |
859 need_clipping = (dl->clip || clip_start > xpos || | |
860 clip_end < xpos + this_width); | |
861 | |
862 /* XDrawImageString only clears the area equal to the height of | |
863 the given font. It is possible that a font is being displayed | |
864 on a line taller than it is, so this would cause us to fail to | |
865 clear some areas. */ | |
866 if ((int) fi->height < (int) (height + dl->clip)) | |
867 { | |
868 int clear_start = max (xpos, clip_start); | |
869 int clear_end = min (xpos + this_width, clip_end); | |
870 | |
871 if (cursor) | |
872 { | |
873 int ypos1_line, ypos1_string, ypos2_line, ypos2_string; | |
874 | |
875 ypos1_string = dl->ypos - fi->ascent; | |
876 ypos2_string = dl->ypos + fi->descent; | |
877 ypos1_line = dl->ypos - dl->ascent; | |
878 ypos2_line = dl->ypos + dl->descent - dl->clip; | |
879 | |
880 /* Make sure we don't clear below the real bottom of the | |
881 line. */ | |
882 if (ypos1_string > ypos2_line) | |
883 ypos1_string = ypos2_line; | |
884 if (ypos2_string > ypos2_line) | |
885 ypos2_string = ypos2_line; | |
886 | |
887 if (ypos1_line < ypos1_string) | |
888 { | |
889 x_clear_region (window, findex, clear_start, ypos1_line, | |
890 clear_end - clear_start, | |
891 ypos1_string - ypos1_line); | |
892 } | |
893 | |
894 if (ypos2_line > ypos2_string) | |
895 { | |
896 x_clear_region (window, findex, clear_start, ypos2_string, | |
897 clear_end - clear_start, | |
898 ypos2_line - ypos2_string); | |
899 } | |
900 } | |
901 else | |
902 { | |
903 x_clear_region (window, findex, clear_start, | |
904 dl->ypos - dl->ascent, clear_end - clear_start, | |
905 height); | |
906 } | |
907 } | |
908 | |
909 if (cursor && cursor_cachel && focus && NILP (bar_cursor_value)) | |
910 gc = x_get_gc (d, font, cursor_cachel->foreground, | |
911 cursor_cachel->background, Qnil, Qnil); | |
912 else | |
913 gc = x_get_gc (d, font, cachel->foreground, cachel->background, | |
914 Qnil, Qnil); | |
915 | |
916 if (need_clipping) | |
917 { | |
918 XRectangle clip_box[1]; | |
919 | |
920 clip_box[0].x = 0; | |
921 clip_box[0].y = 0; | |
922 clip_box[0].width = clip_end - clip_start; | |
923 clip_box[0].height = height; | |
924 | |
925 XSetClipRectangles (dpy, gc, clip_start, dl->ypos - dl->ascent, | |
926 clip_box, 1, Unsorted); | |
927 } | |
928 | |
929 if (runs[i].dimension == 1) | |
930 (bgc ? XDrawString : XDrawImageString) (dpy, x_win, gc, xpos, | |
931 dl->ypos, (char *) runs[i].ptr, | |
932 runs[i].len); | |
933 else | |
934 (bgc ? XDrawString16 : XDrawImageString16) (dpy, x_win, gc, xpos, | |
935 dl->ypos, | |
936 (XChar2b *) runs[i].ptr, | |
937 runs[i].len); | |
938 | |
939 /* We draw underlines in the same color as the text. */ | |
940 if (cachel->underline) | |
941 { | |
942 unsigned long upos, uthick; | |
943 XFontStruct *xfont; | |
944 | |
945 xfont = FONT_INSTANCE_X_FONT (XFONT_INSTANCE (font)); | |
946 if (!XGetFontProperty (xfont, XA_UNDERLINE_POSITION, &upos)) | |
947 upos = 0; | |
948 if (!XGetFontProperty (xfont, XA_UNDERLINE_THICKNESS, &uthick)) | |
949 uthick = 1; | |
950 | |
951 if (dl->ypos + upos < dl->ypos + dl->descent - dl->clip) | |
952 { | |
953 if (dl->ypos + upos + uthick > dl->ypos + dl->descent - dl->clip) | |
954 uthick = dl->descent - dl->clip - upos; | |
955 | |
956 if (uthick == 1) | |
957 { | |
958 XDrawLine (dpy, x_win, gc, xpos, dl->ypos + upos, | |
959 xpos + this_width, dl->ypos + upos); | |
960 } | |
961 else if (uthick > 1) | |
962 { | |
963 XFillRectangle (dpy, x_win, gc, xpos, | |
964 dl->ypos + upos, this_width, uthick); | |
965 } | |
966 } | |
967 } | |
968 | |
969 if (cachel->strikethru) { | |
970 unsigned long ascent,descent,upos, uthick; | |
971 XFontStruct *xfont; | |
972 | |
973 xfont = FONT_INSTANCE_X_FONT (XFONT_INSTANCE (font)); | |
974 | |
975 if (!XGetFontProperty (xfont, XA_STRIKEOUT_ASCENT, &ascent)) | |
976 ascent = xfont->ascent; | |
977 if (!XGetFontProperty (xfont, XA_STRIKEOUT_DESCENT, &descent)) | |
978 descent = xfont->descent; | |
979 if (!XGetFontProperty (xfont, XA_UNDERLINE_THICKNESS, &uthick)) | |
980 uthick = 1; | |
981 | |
982 upos = ascent - ((ascent + descent) / 2) + 1; | |
983 | |
984 /* Generally, upos will be positive (above the baseline),so subtract */ | |
985 if (dl->ypos - upos < dl->ypos + dl->descent - dl->clip) | |
986 { | |
987 if (dl->ypos - upos + uthick > dl->ypos + dl->descent - dl->clip) | |
988 uthick = dl->descent - dl->clip + upos; | |
989 | |
990 if (uthick == 1) | |
991 { | |
992 XDrawLine (dpy, x_win, gc, xpos, dl->ypos - upos, | |
993 xpos + this_width, dl->ypos - upos); | |
994 } | |
995 else if (uthick > 1) | |
996 { | |
997 XFillRectangle (dpy, x_win, gc, xpos, dl->ypos + upos, | |
998 this_width, uthick); | |
999 } | |
1000 } | |
1001 } | |
1002 | |
1003 /* Restore the GC */ | |
1004 if (need_clipping) | |
1005 { | |
1006 XSetClipMask (dpy, gc, None); | |
1007 XSetClipOrigin (dpy, gc, 0, 0); | |
1008 } | |
1009 | |
1010 /* If we are actually superimposing the cursor then redraw with just | |
1011 the appropriate section highlighted. */ | |
1012 if (cursor_clip && !cursor && focus && cursor_cachel) | |
1013 { | |
1014 GC cgc; | |
1015 XRectangle clip_box[1]; | |
1016 | |
1017 cgc = x_get_gc (d, font, cursor_cachel->foreground, | |
1018 cursor_cachel->background, Qnil, Qnil); | |
1019 | |
1020 clip_box[0].x = 0; | |
1021 clip_box[0].y = 0; | |
1022 clip_box[0].width = cursor_width; | |
1023 clip_box[0].height = height; | |
1024 | |
1025 XSetClipRectangles (dpy, cgc, cursor_start, dl->ypos - dl->ascent, | |
1026 clip_box, 1, Unsorted); | |
1027 | |
1028 if (runs[i].dimension == 1) | |
1029 XDrawImageString (dpy, x_win, cgc, xpos, dl->ypos, | |
1030 (char *) runs[i].ptr, runs[i].len); | |
1031 else | |
1032 XDrawImageString16 (dpy, x_win, cgc, xpos, dl->ypos, | |
1033 (XChar2b *) runs[i].ptr, runs[i].len); | |
1034 | |
1035 XSetClipMask (dpy, cgc, None); | |
1036 XSetClipOrigin (dpy, cgc, 0, 0); | |
1037 } | |
1038 | |
1039 xpos += this_width; | |
1040 } | |
1041 | |
1042 /* Draw the non-focus box or bar-cursor as needed. */ | |
1043 /* Can't this logic be simplified? */ | |
1044 if (cursor_cachel | |
1045 && ((cursor && !focus && NILP (bar_cursor_value)) | |
1046 || (cursor_width | |
1047 && (cursor_start + cursor_width >= clip_start) | |
1048 && !NILP (bar_cursor_value)))) | |
1049 { | |
1050 int tmp_height, tmp_y; | |
1051 int bar_width = EQ (bar_cursor_value, Qt) ? 1 : 2; | |
1052 int cursor_x; | |
1053 | |
1054 /* #### This value is correct (as far as I know) because | |
1055 all of the times we need to draw this cursor, we will | |
1056 be called with exactly one character, so we know we | |
1057 can always use runs[0]. | |
1058 | |
1059 This is bogus as all hell, however. The cursor handling in | |
1060 this function is way bogus and desperately needs to be | |
1061 cleaned up. (In particular, the drawing of the cursor should | |
1062 really really be separated out of this function. This may be | |
1063 a bit tricky now because this function itself does way too | |
1064 much stuff, a lot of which needs to be moved into | |
1065 redisplay.c) This is the only way to be able to easily add | |
1066 new cursor types or (e.g.) make the bar cursor be able to | |
1067 span two characters instead of overlaying just one. */ | |
1068 int bogusly_obtained_ascent_value = | |
1069 XFONT_INSTANCE (FACE_CACHEL_FONT (cachel, runs[0].charset))->ascent; | |
1070 | |
1071 if (!NILP (bar_cursor_value)) | |
1072 { | |
1073 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, | |
1074 make_int (bar_width)); | |
1075 } | |
1076 else | |
1077 { | |
1078 gc = x_get_gc (d, Qnil, cursor_cachel->background, | |
1079 Qnil, Qnil, Qnil); | |
1080 } | |
1081 | |
1082 if (cursor) | |
1083 cursor_x = clip_start; | |
1084 else | |
1085 cursor_x = cursor_start; | |
1086 | |
1087 tmp_y = dl->ypos - bogusly_obtained_ascent_value; | |
1088 tmp_height = cursor_height; | |
1089 if (tmp_y + tmp_height > (int) (dl->ypos - dl->ascent + height)) | |
1090 { | |
1091 tmp_y = dl->ypos - dl->ascent + height - tmp_height; | |
1092 if (tmp_y < (int) (dl->ypos - dl->ascent)) | |
1093 tmp_y = dl->ypos - dl->ascent; | |
1094 tmp_height = dl->ypos - dl->ascent + height - tmp_y; | |
1095 } | |
1096 | |
1097 if (!focus && NILP (bar_cursor_value)) | |
1098 { | |
1099 XDrawRectangle (dpy, x_win, gc, cursor_x, tmp_y, | |
1100 cursor_width - 1, tmp_height - 1); | |
1101 } | |
1102 else if (focus && !NILP (bar_cursor_value)) | |
1103 { | |
1104 XDrawLine (dpy, x_win, gc, cursor_x + bar_width - 1, tmp_y, | |
1105 cursor_x + bar_width - 1, tmp_y + tmp_height - 1); | |
1106 } | |
1107 } | |
1108 } | |
1109 | |
1110 void | |
1111 x_output_x_pixmap (struct frame *f, struct Lisp_Image_Instance *p, int x, | |
1112 int y, int clip_x, int clip_y, int clip_width, | |
1113 int clip_height, int width, int height, int pixmap_offset, | |
1114 unsigned long fg, unsigned long bg, GC override_gc) | |
1115 { | |
1116 struct device *d = XDEVICE (f->device); | |
1117 Display *dpy = DEVICE_X_DISPLAY (d); | |
1118 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1119 | |
1120 GC gc; | |
1121 XGCValues gcv; | |
1122 unsigned long pixmap_mask; | |
1123 | |
1124 if (!override_gc) | |
1125 { | |
1126 memset (&gcv, ~0, sizeof (XGCValues)); | |
1127 gcv.graphics_exposures = False; | |
1128 gcv.foreground = fg; | |
1129 gcv.background = bg; | |
1130 pixmap_mask = GCForeground | GCBackground | GCGraphicsExposures; | |
1131 | |
1132 if (IMAGE_INSTANCE_X_MASK (p)) | |
1133 { | |
1134 gcv.function = GXcopy; | |
1135 gcv.clip_mask = IMAGE_INSTANCE_X_MASK (p); | |
1136 gcv.clip_x_origin = x; | |
1137 gcv.clip_y_origin = y - pixmap_offset; | |
1138 pixmap_mask |= (GCFunction | GCClipMask | GCClipXOrigin | | |
1139 GCClipYOrigin); | |
1140 } | |
1141 | |
1142 gc = gc_cache_lookup (DEVICE_X_GC_CACHE (d), &gcv, pixmap_mask); | |
1143 } | |
1144 else | |
1145 gc = override_gc; | |
1146 | |
1147 if (clip_x || clip_y) | |
1148 { | |
1149 XRectangle clip_box[1]; | |
1150 | |
1151 clip_box[0].x = clip_x; | |
1152 clip_box[0].y = clip_y; | |
1153 clip_box[0].width = clip_width; | |
1154 clip_box[0].height = clip_height; | |
1155 | |
1156 XSetClipRectangles (dpy, gc, x, y, clip_box, 1, Unsorted); | |
1157 } | |
1158 | |
1159 /* depth of 0 means it's a bitmap, not a pixmap, and we should use | |
1160 XCopyPlane (1 = current foreground color, 0 = background) instead | |
1161 of XCopyArea, which means that the bits in the pixmap are actual | |
1162 pixel values, instead of symbolic of fg/bg. */ | |
1163 if (IMAGE_INSTANCE_PIXMAP_DEPTH (p) > 0) | |
1164 { | |
1165 XCopyArea (dpy, IMAGE_INSTANCE_X_PIXMAP (p), x_win, gc, 0, | |
1166 pixmap_offset, width, | |
1167 height, x, y); | |
1168 } | |
1169 else | |
1170 { | |
1171 XCopyPlane (dpy, IMAGE_INSTANCE_X_PIXMAP (p), x_win, gc, 0, | |
1172 (pixmap_offset < 0 | |
1173 ? 0 | |
1174 : pixmap_offset), | |
1175 width, height, x, | |
1176 (pixmap_offset < 0 | |
1177 ? y - pixmap_offset | |
1178 : y), | |
1179 1L); | |
1180 } | |
1181 | |
1182 if (clip_x || clip_y) | |
1183 { | |
1184 XSetClipMask (dpy, gc, None); | |
1185 XSetClipOrigin (dpy, gc, 0, 0); | |
1186 } | |
1187 } | |
1188 | |
1189 static void | |
1190 x_output_pixmap (struct window *w, struct display_line *dl, | |
1191 Lisp_Object image_instance, int xpos, int xoffset, | |
1192 int start_pixpos, int width, face_index findex, | |
1193 int cursor_start, int cursor_width, int cursor_height) | |
1194 { | |
1195 struct frame *f = XFRAME (w->frame); | |
1196 struct device *d = XDEVICE (f->device); | |
1197 struct Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance); | |
1198 Lisp_Object window; | |
1199 | |
1200 Display *dpy = DEVICE_X_DISPLAY (d); | |
1201 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1202 int lheight = dl->ascent + dl->descent - dl->clip; | |
1203 int pheight = ((int) IMAGE_INSTANCE_PIXMAP_HEIGHT (p) > lheight ? lheight : | |
1204 IMAGE_INSTANCE_PIXMAP_HEIGHT (p)); | |
1205 int pwidth = min (width + xoffset, (int) IMAGE_INSTANCE_PIXMAP_WIDTH (p)); | |
1206 int clip_x, clip_y, clip_width, clip_height; | |
1207 | |
1208 /* The pixmap_offset is used to center the pixmap on lines which are | |
1209 shorter than it is. This results in odd effects when scrolling | |
1210 pixmaps off of the bottom. Let's try not using it. */ | |
1211 #if 0 | |
1212 int pixmap_offset = (int) (IMAGE_INSTANCE_PIXMAP_HEIGHT (p) - lheight) / 2; | |
1213 #else | |
1214 int pixmap_offset = 0; | |
1215 #endif | |
1216 | |
1217 XSETWINDOW (window, w); | |
1218 | |
1219 if ((start_pixpos >= 0 && start_pixpos > xpos) || xoffset) | |
1220 { | |
1221 if (start_pixpos > xpos && start_pixpos > xpos + width) | |
1222 return; | |
1223 | |
1224 clip_x = xoffset; | |
1225 clip_width = width; | |
1226 if (start_pixpos > xpos) | |
1227 { | |
1228 clip_x += (start_pixpos - xpos); | |
1229 clip_width -= (start_pixpos - xpos); | |
1230 } | |
1231 } | |
1232 else | |
1233 { | |
1234 clip_x = 0; | |
1235 clip_width = 0; | |
1236 } | |
1237 | |
1238 /* Place markers for possible future functionality (clipping the top | |
1239 half instead of the bottom half; think pixel scrolling). */ | |
1240 clip_y = 0; | |
1241 clip_height = pheight; | |
1242 | |
1243 /* Clear the area the pixmap is going into. The pixmap itself will | |
1244 always take care of the full width. We don't want to clear where | |
1245 it is going to go in order to avoid flicker. So, all we have to | |
1246 take care of is any area above or below the pixmap. */ | |
1247 /* #### We take a shortcut for now. We know that since we have | |
1248 pixmap_offset hardwired to 0 that the pixmap is against the top | |
1249 edge so all we have to worry about is below it. */ | |
1250 /* #### Unless the pixmap has a mask in which case we have to clear | |
1251 the whole damn thing since we can't yet clear just the area not | |
1252 included in the mask. */ | |
1253 if (((int) (dl->ypos - dl->ascent + pheight) < | |
1254 (int) (dl->ypos + dl->descent - dl->clip)) | |
1255 || IMAGE_INSTANCE_X_MASK (p)) | |
1256 { | |
1257 int clear_x, clear_y, clear_width, clear_height; | |
1258 | |
1259 if (IMAGE_INSTANCE_X_MASK (p)) | |
1260 { | |
1261 clear_y = dl->ypos - dl->ascent; | |
1262 clear_height = lheight; | |
1263 } | |
1264 else | |
1265 { | |
1266 clear_y = dl->ypos - dl->ascent + pheight; | |
1267 clear_height = lheight - pheight; | |
1268 } | |
1269 | |
1270 if (start_pixpos >= 0 && start_pixpos > xpos) | |
1271 { | |
1272 clear_x = start_pixpos; | |
1273 clear_width = xpos + width - start_pixpos; | |
1274 } | |
1275 else | |
1276 { | |
1277 clear_x = xpos; | |
1278 clear_width = width; | |
1279 } | |
1280 | |
1281 x_clear_region (window, findex, clear_x, clear_y, | |
1282 clear_width, clear_height); | |
1283 } | |
1284 | |
1285 /* Output the pixmap. */ | |
1286 { | |
1287 Lisp_Object tmp_pixel; | |
1288 XColor tmp_bcolor, tmp_fcolor; | |
1289 | |
1290 tmp_pixel = WINDOW_FACE_CACHEL_FOREGROUND (w, findex); | |
1291 tmp_fcolor = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
1292 tmp_pixel = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); | |
1293 tmp_bcolor = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
1294 | |
1295 x_output_x_pixmap (f, p, xpos - xoffset, dl->ypos - dl->ascent, clip_x, | |
1296 clip_y, clip_width, clip_height, | |
1297 pwidth, pheight, pixmap_offset, | |
1298 tmp_fcolor.pixel, tmp_bcolor.pixel, 0); | |
1299 } | |
1300 | |
1301 /* Draw a cursor over top of the pixmap. */ | |
1302 if (cursor_width && cursor_height && (cursor_start >= xpos) | |
1303 && !NILP (w->text_cursor_visible_p) | |
1304 && (cursor_start < xpos + pwidth)) | |
1305 { | |
1306 GC gc; | |
1307 int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d)); | |
1308 int y = dl->ypos - dl->ascent; | |
1309 struct face_cachel *cursor_cachel = | |
1310 WINDOW_FACE_CACHEL (w, | |
1311 get_builtin_face_cache_index | |
1312 (w, Vtext_cursor_face)); | |
1313 | |
1314 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, Qnil); | |
1315 | |
1316 if (cursor_width > xpos + pwidth - cursor_start) | |
1317 cursor_width = xpos + pwidth - cursor_start; | |
1318 | |
1319 if (focus) | |
1320 { | |
1321 XFillRectangle (dpy, x_win, gc, cursor_start, y, cursor_width, | |
1322 cursor_height); | |
1323 } | |
1324 else | |
1325 { | |
1326 XDrawRectangle (dpy, x_win, gc, cursor_start, y, cursor_width, | |
1327 cursor_height); | |
1328 } | |
1329 } | |
1330 } | |
1331 | |
1332 /***************************************************************************** | |
1333 x_output_vertical_divider | |
1334 | |
1335 Draw a vertical divider down the left side of the given window. | |
1336 ****************************************************************************/ | |
1337 static void | |
1338 x_output_vertical_divider (struct window *w, int clear) | |
1339 { | |
1340 struct frame *f = XFRAME (w->frame); | |
1341 struct device *d = XDEVICE (f->device); | |
1342 | |
1343 Display *dpy = DEVICE_X_DISPLAY (d); | |
1344 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1345 GC gc; | |
1346 | |
1347 /* We don't use the normal gutter measurements here because the | |
1348 horizontal scrollbars and toolbars do not stretch completely over | |
1349 to the right edge of the window. Only the modeline does. */ | |
1350 int modeline_height = window_modeline_height (w); | |
1351 int x1, x2; | |
1352 int y1, y2; | |
1353 | |
1354 #ifdef HAVE_SCROLLBARS | |
1355 if (f->scrollbar_on_left) | |
1356 #endif | |
1357 x1 = WINDOW_LEFT (w); | |
1358 #ifdef HAVE_SCROLLBARS | |
1359 else | |
1360 x1 = WINDOW_RIGHT (w) - X_DIVIDER_WIDTH; | |
1361 x2 = x1 + X_DIVIDER_SPACING; | |
1362 #endif | |
1363 | |
1364 #ifdef HAVE_SCROLLBARS | |
1365 if (f->scrollbar_on_top) | |
1366 y1 = WINDOW_TOP (w); | |
1367 else | |
1368 #endif | |
1369 y1 = WINDOW_TEXT_TOP (w); | |
1370 y2 = WINDOW_BOTTOM (w) - modeline_height; | |
1371 | |
1372 /* Draw the divider in the window. */ | |
1373 { | |
1374 /* Clear the divider area first. This needs to be done when a | |
1375 window split occurs. */ | |
1376 if (clear) | |
1377 XClearArea (dpy, x_win, x1, y1, X_DIVIDER_WIDTH, y2 - y1, False); | |
1378 | |
1379 /* #### There needs to be some checks to make sure that whatever | |
1380 colors we choose, the line will be visible (not same color as | |
1381 default background. | |
1382 | |
1383 #### No there don't. If I want the vertical divider to be | |
1384 invisible, I should be able to make it so. */ | |
1385 gc = x_get_gc (d, Qnil, WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX), | |
1386 WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX), | |
1387 Qnil, Qnil); | |
1388 | |
1389 /* Draw the divider line. */ | |
1390 XFillRectangle (dpy, x_win, gc, x2, y1, X_DIVIDER_LINE_WIDTH, y2 - y1); | |
1391 } | |
1392 | |
1393 /* Draw the divider in the modeline but only if we are using 2D | |
1394 modelines. */ | |
1395 if (EQ (Qzero, w->modeline_shadow_thickness)) | |
1396 { | |
1397 XFillRectangle (dpy, x_win, gc, x1, y2, X_DIVIDER_WIDTH, | |
1398 modeline_height); | |
1399 | |
1400 /* #### There needs to be some checks to make sure that whatever | |
1401 colors we choose, the line will be visible (not same color as | |
1402 default background. */ | |
1403 gc = x_get_gc (d, Qnil, | |
1404 WINDOW_FACE_CACHEL_FOREGROUND (w, MODELINE_INDEX), | |
1405 WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX), | |
1406 Qnil, Qnil); | |
1407 | |
1408 /* Draw the divider line. */ | |
1409 XFillRectangle (dpy, x_win, gc, x2, y2, X_DIVIDER_LINE_WIDTH, | |
1410 modeline_height); | |
1411 } | |
1412 } | |
1413 | |
1414 /***************************************************************************** | |
1415 x_output_blank | |
1416 | |
1417 Output a blank by clearing the area it covers in the foreground color | |
1418 of its face. | |
1419 ****************************************************************************/ | |
1420 static void | |
1421 x_output_blank (struct window *w, struct display_line *dl, struct rune *rb, | |
1422 int start_pixpos, int cursor_start, int cursor_width) | |
1423 { | |
1424 struct frame *f = XFRAME (w->frame); | |
1425 struct device *d = XDEVICE (f->device); | |
1426 | |
1427 Display *dpy = DEVICE_X_DISPLAY (d); | |
1428 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1429 GC gc; | |
1430 struct face_cachel *cursor_cachel = | |
1431 WINDOW_FACE_CACHEL (w, | |
1432 get_builtin_face_cache_index | |
1433 (w, Vtext_cursor_face)); | |
1434 Lisp_Object bg_pmap; | |
1435 Lisp_Object buffer = WINDOW_BUFFER (w); | |
1436 Lisp_Object bar_cursor_value = symbol_value_in_buffer (Qbar_cursor, | |
1437 buffer); | |
1438 | |
1439 int x = rb->xpos; | |
1440 int y = dl->ypos - dl->ascent; | |
1441 int width = rb->width; | |
1442 int height = dl->ascent + dl->descent - dl->clip; | |
1443 | |
1444 if (start_pixpos > x) | |
1445 { | |
1446 if (start_pixpos >= (x + width)) | |
1447 return; | |
1448 else | |
1449 { | |
1450 width -= (start_pixpos - x); | |
1451 x = start_pixpos; | |
1452 } | |
1453 } | |
1454 | |
1455 bg_pmap = WINDOW_FACE_CACHEL_BACKGROUND_PIXMAP (w, rb->findex); | |
1456 if (!IMAGE_INSTANCEP (bg_pmap) | |
1457 || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap))) | |
1458 bg_pmap = Qnil; | |
1459 | |
1460 if (NILP (bg_pmap)) | |
1461 gc = x_get_gc (d, Qnil, WINDOW_FACE_CACHEL_BACKGROUND (w, rb->findex), | |
1462 Qnil, Qnil, Qnil); | |
1463 else | |
1464 gc = x_get_gc (d, Qnil, WINDOW_FACE_CACHEL_FOREGROUND (w, rb->findex), | |
1465 WINDOW_FACE_CACHEL_BACKGROUND (w, rb->findex), bg_pmap, | |
1466 Qnil); | |
1467 | |
1468 XFillRectangle (dpy, x_win, gc, x, y, width, height); | |
1469 | |
1470 /* If this rune is marked as having the cursor, then it is actually | |
1471 representing a tab. */ | |
1472 if (!NILP (w->text_cursor_visible_p) | |
1473 && (rb->cursor_type == CURSOR_ON | |
1474 || (cursor_width | |
1475 && (cursor_start + cursor_width > x) | |
1476 && cursor_start < (x + width)))) | |
1477 { | |
1478 int cursor_height, cursor_y; | |
1479 int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d)); | |
1480 struct Lisp_Font_Instance *fi; | |
1481 | |
1482 fi = XFONT_INSTANCE (FACE_CACHEL_FONT | |
1483 (WINDOW_FACE_CACHEL (w, rb->findex), | |
1484 Vcharset_ascii)); | |
1485 | |
1486 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, Qnil); | |
1487 | |
1488 cursor_y = dl->ypos - fi->ascent; | |
1489 cursor_height = fi->height; | |
1490 if (cursor_y + cursor_height > y + height) | |
1491 cursor_height = y + height - cursor_y; | |
1492 | |
1493 if (focus) | |
1494 { | |
1495 if (NILP (bar_cursor_value)) | |
1496 { | |
1497 XFillRectangle (dpy, x_win, gc, cursor_start, cursor_y, | |
1498 fi->width, cursor_height); | |
1499 } | |
1500 else | |
1501 { | |
1502 int bar_width = EQ (bar_cursor_value, Qt) ? 1 : 2; | |
1503 | |
1504 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, | |
1505 make_int (bar_width)); | |
1506 XDrawLine (dpy, x_win, gc, cursor_start + bar_width - 1, | |
1507 cursor_y, cursor_start + bar_width - 1, | |
1508 cursor_y + cursor_height - 1); | |
1509 } | |
1510 } | |
1511 else if (NILP (bar_cursor_value)) | |
1512 { | |
1513 XDrawRectangle (dpy, x_win, gc, cursor_start, cursor_y, | |
1514 fi->width - 1, cursor_height - 1); | |
1515 } | |
1516 } | |
1517 } | |
1518 | |
1519 /***************************************************************************** | |
1520 x_output_hline | |
1521 | |
1522 Output a horizontal line in the foreground of its face. | |
1523 ****************************************************************************/ | |
1524 static void | |
1525 x_output_hline (struct window *w, struct display_line *dl, struct rune *rb) | |
1526 { | |
1527 struct frame *f = XFRAME (w->frame); | |
1528 struct device *d = XDEVICE (f->device); | |
1529 | |
1530 Display *dpy = DEVICE_X_DISPLAY (d); | |
1531 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1532 GC gc; | |
1533 | |
1534 int x = rb->xpos; | |
1535 int width = rb->width; | |
1536 int height = dl->ascent + dl->descent - dl->clip; | |
1537 int ypos1, ypos2, ypos3, ypos4; | |
1538 | |
1539 ypos1 = dl->ypos - dl->ascent; | |
1540 ypos2 = ypos1 + rb->object.hline.yoffset; | |
1541 ypos3 = ypos2 + rb->object.hline.thickness; | |
1542 ypos4 = dl->ypos + dl->descent - dl->clip; | |
1543 | |
1544 /* First clear the area not covered by the line. */ | |
1545 if (height - rb->object.hline.thickness > 0) | |
1546 { | |
1547 gc = x_get_gc (d, Qnil, WINDOW_FACE_CACHEL_FOREGROUND (w, rb->findex), | |
1548 Qnil, Qnil, Qnil); | |
1549 | |
1550 if (ypos2 - ypos1 > 0) | |
1551 XFillRectangle (dpy, x_win, gc, x, ypos1, width, ypos2 - ypos1); | |
1552 if (ypos4 - ypos3 > 0) | |
1553 XFillRectangle (dpy, x_win, gc, x, ypos1, width, ypos2 - ypos1); | |
1554 } | |
1555 | |
1556 /* Now draw the line. */ | |
1557 gc = x_get_gc (d, Qnil, WINDOW_FACE_CACHEL_BACKGROUND (w, rb->findex), | |
1558 Qnil, Qnil, Qnil); | |
1559 | |
1560 if (ypos2 < ypos1) | |
1561 ypos2 = ypos1; | |
1562 if (ypos3 > ypos4) | |
1563 ypos3 = ypos4; | |
1564 | |
1565 if (ypos3 - ypos2 > 0) | |
1566 XFillRectangle (dpy, x_win, gc, x, ypos2, width, ypos3 - ypos2); | |
1567 } | |
1568 | |
1569 /***************************************************************************** | |
1570 x_output_shadows | |
1571 | |
1572 Draw a shadow around the given area using the given GC's. It is the | |
1573 callers responsibility to ste the GC's appropriately. | |
1574 ****************************************************************************/ | |
1575 void | |
1576 x_output_shadows (struct frame *f, int x, int y, int width, int height, | |
1577 GC top_shadow_gc, GC bottom_shadow_gc, GC background_gc, | |
1578 int shadow_thickness) | |
1579 { | |
1580 struct device *d = XDEVICE (f->device); | |
1581 | |
1582 Display *dpy = DEVICE_X_DISPLAY (d); | |
1583 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1584 | |
1585 XSegment top_shadow[20], bottom_shadow[20]; | |
1586 int elt; | |
1587 | |
1588 if (shadow_thickness > 10) | |
1589 shadow_thickness = 10; | |
1590 else if (shadow_thickness < 0) | |
1591 shadow_thickness = 0; | |
1592 if (shadow_thickness > (width / 2)) | |
1593 shadow_thickness = width / 2; | |
1594 if (shadow_thickness > (height / 2)) | |
1595 shadow_thickness = height / 2; | |
1596 | |
1597 for (elt = 0; elt < shadow_thickness; elt++) | |
1598 { | |
1599 int seg1 = elt; | |
1600 int seg2 = elt + shadow_thickness; | |
1601 | |
1602 top_shadow[seg1].x1 = x; | |
1603 top_shadow[seg1].x2 = x + width - elt - 1; | |
1604 top_shadow[seg1].y1 = top_shadow[seg1].y2 = y + elt; | |
1605 | |
1606 top_shadow[seg2].x1 = top_shadow[seg2].x2 = x + elt; | |
1607 top_shadow[seg2].y1 = y + shadow_thickness; | |
1608 top_shadow[seg2].y2 = y + height - elt - 1; | |
1609 | |
1610 bottom_shadow[seg1].x1 = x + elt + 1; | |
1611 bottom_shadow[seg1].x2 = x + width - 1; | |
1612 bottom_shadow[seg1].y1 = bottom_shadow[seg1].y2 = y + height - elt - 1; | |
1613 | |
1614 bottom_shadow[seg2].x1 = bottom_shadow[seg2].x2 = x + width - elt - 1; | |
1615 bottom_shadow[seg2].y1 = y + elt + 1; | |
1616 bottom_shadow[seg2].y2 = y + height - shadow_thickness; | |
1617 } | |
1618 | |
1619 XDrawSegments (dpy, x_win, top_shadow_gc, top_shadow, shadow_thickness * 2); | |
1620 XDrawSegments (dpy, x_win, bottom_shadow_gc, bottom_shadow, | |
1621 shadow_thickness * 2); | |
1622 } | |
1623 | |
1624 /***************************************************************************** | |
1625 x_generate_shadow_pixels | |
1626 | |
1627 Given three pixels (top shadow, bottom shadow, background) massage | |
1628 the top and bottom shadow colors to guarantee that they differ. The | |
1629 background pixels are not allowed to be modified. | |
1630 | |
1631 This function modifies its parameters. | |
1632 | |
1633 This code is modified from code blatantly stolen from lwlib/xlwmenu.c | |
1634 ****************************************************************************/ | |
1635 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \ | |
1636 ? ((unsigned long) (x)) : ((unsigned long) (y))) | |
1637 | |
1638 void | |
1639 x_generate_shadow_pixels (struct frame *f, unsigned long *top_shadow, | |
1640 unsigned long *bottom_shadow, | |
1641 unsigned long background, | |
1642 unsigned long core_background) | |
1643 { | |
1644 struct device *d = XDEVICE (f->device); | |
1645 Display *dpy = DEVICE_X_DISPLAY (d); | |
1646 Colormap cmap = | |
1647 DefaultColormapOfScreen (XtScreen ((Widget) FRAME_X_TEXT_WIDGET (f))); | |
1648 | |
1649 XColor topc, botc; | |
1650 int top_frobbed = 0, bottom_frobbed = 0; | |
1651 | |
1652 /* If the top shadow is the same color as the background, try and | |
1653 adjust it. */ | |
1654 if (*top_shadow == background) | |
1655 { | |
1656 topc.pixel = background; | |
1657 XQueryColor (dpy, cmap, &topc); | |
1658 /* don't overflow/wrap! */ | |
1659 topc.red = MINL (65535, topc.red * 1.2); | |
1660 topc.green = MINL (65535, topc.green * 1.2); | |
1661 topc.blue = MINL (65535, topc.blue * 1.2); | |
1662 if (allocate_nearest_color (dpy, cmap, &topc)) | |
1663 { | |
1664 *top_shadow = topc.pixel; | |
1665 top_frobbed = 1; | |
1666 } | |
1667 } | |
1668 | |
1669 /* If the bottom shadow is the same color as the background, try and | |
1670 adjust it. */ | |
1671 if (*bottom_shadow == background) | |
1672 { | |
1673 botc.pixel = background; | |
1674 XQueryColor (dpy, cmap, &botc); | |
1675 botc.red *= 0.6; | |
1676 botc.green *= 0.6; | |
1677 botc.blue *= 0.6; | |
1678 if (allocate_nearest_color (dpy, cmap, &botc)) | |
1679 { | |
1680 *bottom_shadow = botc.pixel; | |
1681 bottom_frobbed = 1; | |
1682 } | |
1683 } | |
1684 | |
1685 /* If we had to adjust both shadows, then we have to do some | |
1686 additional work. */ | |
1687 if (top_frobbed && bottom_frobbed) | |
1688 { | |
1689 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3)); | |
1690 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3)); | |
1691 if (bot_avg > top_avg) | |
1692 { | |
1693 Pixel tmp = *top_shadow; | |
1694 | |
1695 *top_shadow = *bottom_shadow; | |
1696 *bottom_shadow = tmp; | |
1697 } | |
1698 else if (topc.pixel == botc.pixel) | |
1699 { | |
1700 if (botc.pixel == background) | |
1701 *top_shadow = core_background; | |
1702 else | |
1703 *bottom_shadow = background; | |
1704 } | |
1705 } | |
1706 } | |
1707 | |
1708 /***************************************************************************** | |
1709 x_clear_to_window_end | |
1710 | |
1711 Clear the area between ypos1 and ypos2. Each margin area and the | |
1712 text area is handled separately since they may each have their own | |
1713 background color. | |
1714 ****************************************************************************/ | |
1715 static void | |
1716 x_clear_to_window_end (struct window *w, int ypos1, int ypos2) | |
1717 { | |
1718 int height = ypos2 - ypos1; | |
1719 | |
1720 if (height) | |
1721 { | |
1722 struct frame *f = XFRAME (w->frame); | |
1723 Lisp_Object window; | |
1724 int bflag = (window_needs_vertical_divider (w) ? 0 : 1); | |
1725 layout_bounds bounds; | |
1726 | |
1727 bounds = calculate_display_line_boundaries (w, bflag); | |
1728 XSETWINDOW (window, w); | |
1729 | |
1730 if (window_is_leftmost (w)) | |
1731 x_clear_region (window, DEFAULT_INDEX, FRAME_LEFT_BORDER_START (f), | |
1732 ypos1, FRAME_BORDER_WIDTH (f), height); | |
1733 | |
1734 if (bounds.left_in - bounds.left_out > 0) | |
1735 x_clear_region (window, | |
1736 get_builtin_face_cache_index (w, Vleft_margin_face), | |
1737 bounds.left_out, ypos1, | |
1738 bounds.left_in - bounds.left_out, height); | |
1739 | |
1740 if (bounds.right_in - bounds.left_in > 0) | |
1741 x_clear_region (window, DEFAULT_INDEX, bounds.left_in, ypos1, | |
1742 bounds.right_in - bounds.left_in, height); | |
1743 | |
1744 if (bounds.right_out - bounds.right_in > 0) | |
1745 x_clear_region (window, | |
1746 get_builtin_face_cache_index (w, Vright_margin_face), | |
1747 bounds.right_in, ypos1, | |
1748 bounds.right_out - bounds.right_in, height); | |
1749 | |
1750 if (window_is_rightmost (w)) | |
1751 x_clear_region (window, DEFAULT_INDEX, FRAME_RIGHT_BORDER_START (f), | |
1752 ypos1, FRAME_BORDER_WIDTH (f), height); | |
1753 } | |
1754 } | |
1755 | |
1756 /***************************************************************************** | |
1757 x_redraw_exposed_window | |
1758 | |
1759 Given a bounding box for an area that needs to be redrawn, determine | |
1760 what parts of what lines are contained within and re-output their | |
1761 contents. | |
1762 ****************************************************************************/ | |
1763 static void | |
1764 x_redraw_exposed_window (struct window *w, int x, int y, int width, int height) | |
1765 { | |
1766 struct frame *f = XFRAME (w->frame); | |
1767 int line; | |
1768 int start_x, start_y, end_x, end_y; | |
1769 int orig_windows_structure_changed; | |
1770 | |
1771 display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP); | |
1772 | |
1773 if (!NILP (w->vchild)) | |
1774 { | |
1775 x_redraw_exposed_windows (w->vchild, x, y, width, height); | |
1776 return; | |
1777 } | |
1778 else if (!NILP (w->hchild)) | |
1779 { | |
1780 x_redraw_exposed_windows (w->hchild, x, y, width, height); | |
1781 return; | |
1782 } | |
1783 | |
1784 /* If the window doesn't intersect the exposed region, we're done here. */ | |
1785 if (x > WINDOW_RIGHT (w) || (x + width) < WINDOW_LEFT (w) | |
1786 || y > WINDOW_BOTTOM (w) || (y + height) < WINDOW_TOP (w)) | |
1787 { | |
1788 return; | |
1789 } | |
1790 else | |
1791 { | |
1792 start_x = max (WINDOW_LEFT (w), x); | |
1793 end_x = min (WINDOW_RIGHT (w), (x + width)); | |
1794 start_y = max (WINDOW_TOP (w), y); | |
1795 end_y = min (WINDOW_BOTTOM (w), y + height); | |
1796 | |
1797 /* We do this to make sure that the 3D modelines get redrawn if | |
1798 they are in the exposed region. */ | |
1799 orig_windows_structure_changed = f->windows_structure_changed; | |
1800 f->windows_structure_changed = 1; | |
1801 } | |
1802 | |
1803 if (window_needs_vertical_divider (w)) | |
1804 { | |
1805 x_output_vertical_divider (w, 0); | |
1806 } | |
1807 | |
1808 for (line = 0; line < Dynarr_length (cdla); line++) | |
1809 { | |
1810 struct display_line *cdl = Dynarr_atp (cdla, line); | |
1811 int top_y = cdl->ypos - cdl->ascent; | |
1812 int bottom_y = cdl->ypos + cdl->descent; | |
1813 | |
1814 if (bottom_y >= start_y) | |
1815 { | |
1816 if (top_y > end_y) | |
1817 { | |
1818 if (line == 0) | |
1819 continue; | |
1820 else | |
1821 break; | |
1822 } | |
1823 else | |
1824 { | |
1825 output_display_line (w, 0, cdla, line, start_x, end_x); | |
1826 } | |
1827 } | |
1828 } | |
1829 | |
1830 f->windows_structure_changed = orig_windows_structure_changed; | |
1831 | |
1832 /* If there have never been any face cache_elements created, then this | |
1833 expose event doesn't actually have anything to do. */ | |
1834 if (Dynarr_largest (w->face_cachels)) | |
1835 redisplay_clear_bottom_of_window (w, cdla, start_y, end_y); | |
1836 } | |
1837 | |
1838 /***************************************************************************** | |
1839 x_redraw_exposed_windows | |
1840 | |
1841 For each window beneath the given window in the window hierarchy, | |
1842 ensure that it is redrawn if necessary after an Expose event. | |
1843 ****************************************************************************/ | |
1844 static void | |
1845 x_redraw_exposed_windows (Lisp_Object window, int x, int y, int width, | |
1846 int height) | |
1847 { | |
1848 for (; !NILP (window); window = XWINDOW (window)->next) | |
1849 x_redraw_exposed_window (XWINDOW (window), x, y, width, height); | |
1850 } | |
1851 | |
1852 /***************************************************************************** | |
1853 x_redraw_exposed_area | |
1854 | |
1855 For each window on the given frame, ensure that any area in the | |
1856 Exposed area is redrawn. | |
1857 ****************************************************************************/ | |
1858 void | |
1859 x_redraw_exposed_area (struct frame *f, int x, int y, int width, int height) | |
1860 { | |
1861 /* If any window on the frame has had its face cache reset then the | |
1862 redisplay structures are effectively invalid. If we attempt to | |
1863 use them we'll blow up. We mark the frame as changed to ensure | |
1864 that redisplay will do a full update. This probably isn't | |
1865 necessary but it can't hurt. */ | |
1866 | |
1867 #ifdef HAVE_TOOLBARS | |
1868 /* #### We would rather put these off as well but there is currently | |
1869 no combination of flags which will force an unchanged toolbar to | |
1870 redraw anyhow. */ | |
1871 MAYBE_FRAMEMETH (f, redraw_exposed_toolbars, (f, x, y, width, height)); | |
1872 #endif | |
1873 | |
1874 if (!f->window_face_cache_reset) | |
1875 { | |
1876 x_redraw_exposed_windows (f->root_window, x, y, width, height); | |
1877 | |
1878 XFlush (DEVICE_X_DISPLAY (XDEVICE (f->device))); | |
1879 } | |
1880 else | |
1881 MARK_FRAME_CHANGED (f); | |
1882 } | |
1883 | |
1884 /**************************************************************************** | |
1885 x_clear_region | |
1886 | |
1887 Clear the area in the box defined by the given parameters using the | |
1888 given face. | |
1889 ****************************************************************************/ | |
1890 static void | |
1891 x_clear_region (Lisp_Object locale, face_index findex, int x, int y, | |
1892 int width, int height) | |
1893 { | |
1894 struct window *w = 0; | |
1895 struct frame *f = 0; | |
1896 struct device *d; | |
1897 Lisp_Object background_pixmap; | |
1898 | |
1899 Display *dpy; | |
1900 Window x_win; | |
1901 | |
1902 if (WINDOWP (locale)) | |
1903 { | |
1904 w = XWINDOW (locale); | |
1905 f = XFRAME (w->frame); | |
1906 } | |
1907 else if (FRAMEP (locale)) | |
1908 { | |
1909 w = 0; | |
1910 f = XFRAME (locale); | |
1911 } | |
1912 else | |
1913 abort (); | |
1914 | |
1915 d = XDEVICE (f->device); | |
1916 dpy = DEVICE_X_DISPLAY (d); | |
1917 x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
1918 | |
1919 /* #### This function is going to have to be made cursor aware. */ | |
1920 if (width && height) | |
1921 { | |
1922 int values_set = 0; | |
1923 GC gc; | |
1924 | |
1925 /* #### This isn't quite right for when this function is called | |
1926 from the toolbar code. */ | |
1927 background_pixmap = Qunbound; | |
1928 | |
1929 /* Don't use a backing pixmap in the border area */ | |
1930 if (x >= FRAME_LEFT_BORDER_END (f) | |
1931 && x < FRAME_RIGHT_BORDER_START (f) | |
1932 && y >= FRAME_TOP_BORDER_END (f) | |
1933 && y < FRAME_BOTTOM_BORDER_START (f)) | |
1934 { | |
1935 Lisp_Object temp; | |
1936 | |
1937 if (w) | |
1938 { | |
1939 temp = WINDOW_FACE_CACHEL_BACKGROUND_PIXMAP (w, findex); | |
1940 | |
1941 if (IMAGE_INSTANCEP (temp) | |
1942 && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (temp))) | |
1943 { | |
1944 /* #### maybe we could implement such that a string | |
1945 can be a background pixmap? */ | |
1946 background_pixmap = temp; | |
1947 } | |
1948 } | |
1949 else | |
1950 { | |
1951 temp = FACE_BACKGROUND_PIXMAP (Vdefault_face, locale); | |
1952 | |
1953 if (IMAGE_INSTANCEP (temp) | |
1954 && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (temp))) | |
1955 { | |
1956 background_pixmap = temp; | |
1957 } | |
1958 } | |
1959 | |
1960 if (!UNBOUNDP (background_pixmap) && | |
1961 XIMAGE_INSTANCE_PIXMAP_DEPTH (background_pixmap) == 0) | |
1962 { | |
1963 Lisp_Object fcolor, bcolor; | |
1964 | |
1965 if (w) | |
1966 { | |
1967 fcolor = WINDOW_FACE_CACHEL_FOREGROUND (w, findex); | |
1968 bcolor = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); | |
1969 } | |
1970 else | |
1971 { | |
1972 fcolor = FACE_FOREGROUND (Vdefault_face, locale); | |
1973 bcolor = FACE_BACKGROUND (Vdefault_face, locale); | |
1974 } | |
1975 | |
1976 gc = x_get_gc (d, Qnil, fcolor, bcolor, background_pixmap, | |
1977 Qnil); | |
1978 values_set = 1; | |
1979 } | |
1980 else | |
1981 { | |
1982 Lisp_Object color; | |
1983 | |
1984 if (UNBOUNDP (background_pixmap)) | |
1985 background_pixmap = Qnil; | |
1986 | |
1987 if (w) | |
1988 color = WINDOW_FACE_CACHEL_BACKGROUND (w, findex); | |
1989 else | |
1990 color = FACE_BACKGROUND (Vdefault_face, locale); | |
1991 | |
1992 gc = x_get_gc (d, Qnil, color, Qnil, background_pixmap, | |
1993 Qnil); | |
1994 values_set = 1; | |
1995 } | |
1996 } | |
1997 | |
1998 if (values_set) | |
1999 { | |
2000 XFillRectangle (dpy, x_win, gc, x, y, width, height); | |
2001 } | |
2002 else | |
2003 { | |
2004 XClearArea (dpy, x_win, x, y, width, height, False); | |
2005 } | |
2006 } | |
2007 } | |
2008 | |
2009 /***************************************************************************** | |
2010 x_output_eol_cursor | |
2011 | |
2012 Draw a cursor at the end of a line. The end-of-line cursor is | |
2013 narrower than the normal cursor. | |
2014 ****************************************************************************/ | |
2015 static void | |
2016 x_output_eol_cursor (struct window *w, struct display_line *dl, int xpos) | |
2017 { | |
2018 struct frame *f = XFRAME (w->frame); | |
2019 struct device *d = XDEVICE (f->device); | |
2020 | |
2021 Display *dpy = DEVICE_X_DISPLAY (d); | |
2022 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
2023 GC gc; | |
2024 face_index elt = get_builtin_face_cache_index (w, Vtext_cursor_face); | |
2025 struct face_cachel *cursor_cachel = WINDOW_FACE_CACHEL (w, elt); | |
2026 | |
2027 int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d)); | |
2028 Lisp_Object bar_cursor_value = symbol_value_in_buffer (Qbar_cursor, | |
2029 WINDOW_BUFFER (w)); | |
2030 | |
2031 int x = xpos; | |
2032 int y = dl->ypos - dl->ascent; | |
2033 int width = EOL_CURSOR_WIDTH; | |
2034 int height = dl->ascent + dl->descent - dl->clip; | |
2035 int cursor_height, cursor_y; | |
2036 int defheight, defascent; | |
2037 | |
2038 XClearArea (dpy, x_win, x, y, width, height, False); | |
2039 | |
2040 if (NILP (w->text_cursor_visible_p)) | |
2041 return; | |
2042 | |
2043 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, Qnil); | |
2044 | |
2045 { | |
2046 Lisp_Object window = Qnil; | |
2047 | |
2048 XSETWINDOW (window, w); | |
2049 default_face_font_info (window, &defascent, 0, &defheight, 0, 0); | |
2050 } | |
2051 | |
2052 cursor_y = dl->ypos - defascent; | |
2053 if (cursor_y < y) | |
2054 cursor_y = y; | |
2055 cursor_height = defheight; | |
2056 if (cursor_y + cursor_height > y + height) | |
2057 cursor_height = y + height - cursor_y; | |
2058 | |
2059 if (focus) | |
2060 { | |
2061 if (NILP (bar_cursor_value)) | |
2062 { | |
2063 XFillRectangle (dpy, x_win, gc, x, cursor_y, width, cursor_height); | |
2064 } | |
2065 else | |
2066 { | |
2067 int bar_width = EQ (bar_cursor_value, Qt) ? 1 : 2; | |
2068 | |
2069 gc = x_get_gc (d, Qnil, cursor_cachel->background, Qnil, Qnil, | |
2070 make_int (bar_width)); | |
2071 XDrawLine (dpy, x_win, gc, x + bar_width - 1, cursor_y, | |
2072 x + bar_width - 1, cursor_y + cursor_height - 1); | |
2073 } | |
2074 } | |
2075 else if (NILP (bar_cursor_value)) | |
2076 { | |
2077 XDrawRectangle (dpy, x_win, gc, x, cursor_y, width - 1, | |
2078 cursor_height - 1); | |
2079 } | |
2080 } | |
2081 | |
2082 static void | |
2083 x_clear_frame_window (Lisp_Object window) | |
2084 { | |
2085 struct window *w = XWINDOW (window); | |
2086 | |
2087 if (!NILP (w->vchild)) | |
2088 { | |
2089 x_clear_frame_windows (w->vchild); | |
2090 return; | |
2091 } | |
2092 | |
2093 if (!NILP (w->hchild)) | |
2094 { | |
2095 x_clear_frame_windows (w->hchild); | |
2096 return; | |
2097 } | |
2098 | |
2099 x_clear_to_window_end (w, WINDOW_TEXT_TOP (w), WINDOW_TEXT_BOTTOM (w)); | |
2100 } | |
2101 | |
2102 static void | |
2103 x_clear_frame_windows (Lisp_Object window) | |
2104 { | |
2105 for (; !NILP (window); window = XWINDOW (window)->next) | |
2106 x_clear_frame_window (window); | |
2107 } | |
2108 | |
2109 static void | |
2110 x_clear_frame (struct frame *f) | |
2111 { | |
2112 struct device *d = XDEVICE (f->device); | |
2113 Display *dpy = DEVICE_X_DISPLAY (d); | |
2114 Window x_win = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
2115 int x, y, width, height; | |
2116 Lisp_Object frame; | |
2117 | |
2118 x = FRAME_LEFT_BORDER_START (f); | |
2119 width = (FRAME_PIXWIDTH (f) - FRAME_REAL_LEFT_TOOLBAR_WIDTH (f) - | |
2120 FRAME_REAL_RIGHT_TOOLBAR_WIDTH (f)); | |
2121 /* #### This adjustment by 1 should be being done in the macros. | |
2122 There is some small differences between when the menubar is on | |
2123 and off that we still need to deal with. */ | |
2124 y = FRAME_TOP_BORDER_START (f) - 1; | |
2125 height = (FRAME_PIXHEIGHT (f) - FRAME_REAL_TOP_TOOLBAR_HEIGHT (f) - | |
2126 FRAME_REAL_BOTTOM_TOOLBAR_HEIGHT (f)) + 1; | |
2127 | |
2128 XClearArea (dpy, x_win, x, y, width, height, False); | |
2129 | |
2130 XSETFRAME (frame, f); | |
2131 | |
2132 if (!UNBOUNDP (FACE_BACKGROUND_PIXMAP (Vdefault_face, frame)) | |
2133 || !UNBOUNDP (FACE_BACKGROUND_PIXMAP (Vleft_margin_face, frame)) | |
2134 || !UNBOUNDP (FACE_BACKGROUND_PIXMAP (Vright_margin_face, frame))) | |
2135 { | |
2136 x_clear_frame_windows (f->root_window); | |
2137 } | |
2138 | |
2139 XFlush (DEVICE_X_DISPLAY (d)); | |
2140 } | |
2141 | |
2142 /* briefly swap the foreground and background colors. | |
2143 */ | |
2144 | |
2145 static int | |
2146 x_flash (struct device *d) | |
2147 { | |
2148 Display *dpy; | |
2149 Window w; | |
2150 XGCValues gcv; | |
2151 GC gc; | |
2152 XColor tmp_fcolor, tmp_bcolor; | |
2153 Lisp_Object tmp_pixel, frame; | |
2154 struct frame *f = device_selected_frame (d); | |
2155 Widget shell = FRAME_X_SHELL_WIDGET (f); | |
2156 Dimension width, height; | |
2157 | |
2158 XtVaGetValues (shell, XtNwidth, &width, XtNheight, &height, 0); | |
2159 XSETFRAME (frame, f); | |
2160 | |
2161 tmp_pixel = FACE_FOREGROUND (Vdefault_face, frame); | |
2162 tmp_fcolor = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
2163 tmp_pixel = FACE_BACKGROUND (Vdefault_face, frame); | |
2164 tmp_bcolor = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (tmp_pixel)); | |
2165 | |
2166 dpy = XtDisplay (shell); | |
2167 w = XtWindow (FRAME_X_TEXT_WIDGET (f)); | |
2168 memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */ | |
2169 gcv.foreground = (tmp_fcolor.pixel ^ tmp_bcolor.pixel); | |
2170 gcv.function = GXxor; | |
2171 gcv.graphics_exposures = False; | |
2172 gc = gc_cache_lookup (DEVICE_X_GC_CACHE (XDEVICE (f->device)), &gcv, | |
2173 (GCForeground | GCFunction | GCGraphicsExposures)); | |
2174 XFillRectangle (dpy, w, gc, 0, 0, width, height); | |
2175 XSync (dpy, False); | |
2176 | |
2177 #ifdef HAVE_POLL | |
2178 poll (0, 0, 100); | |
2179 #else /* !HAVE_POLL */ | |
2180 { | |
2181 int usecs = 100000; | |
2182 struct timeval tv; | |
2183 tv.tv_sec = usecs / 1000000L; | |
2184 tv.tv_usec = usecs % 1000000L; | |
2185 /* I'm sure someone is going to complain about this... */ | |
2186 (void) select (0, 0, 0, 0, &tv); | |
2187 } | |
2188 #endif /* !HAVE_POLL */ | |
2189 | |
2190 XFillRectangle (dpy, w, gc, 0, 0, width, height); | |
2191 XSync (dpy, False); | |
2192 | |
2193 return 1; | |
2194 } | |
2195 | |
2196 /* Make audible bell. */ | |
2197 | |
2198 static void | |
2199 x_ring_bell (struct device *d, int volume, int pitch, int duration) | |
2200 { | |
2201 Display *display = DEVICE_X_DISPLAY (d); | |
2202 | |
2203 if (volume < 0) volume = 0; | |
2204 else if (volume > 100) volume = 100; | |
2205 if (pitch < 0 && duration < 0) | |
2206 { | |
2207 XBell (display, (volume * 2) - 100); | |
2208 XFlush (display); | |
2209 } | |
2210 else | |
2211 { | |
2212 XKeyboardState state; | |
2213 XKeyboardControl ctl; | |
2214 XSync (display, 0); | |
2215 /* #### grab server? */ | |
2216 XGetKeyboardControl (display, &state); | |
2217 | |
2218 ctl.bell_pitch = (pitch >= 0 ? pitch : state.bell_pitch); | |
2219 ctl.bell_duration = (duration >= 0 ? duration : state.bell_duration); | |
2220 XChangeKeyboardControl (display, KBBellPitch|KBBellDuration, &ctl); | |
2221 | |
2222 XBell (display, (volume * 2) - 100); | |
2223 | |
2224 ctl.bell_pitch = state.bell_pitch; | |
2225 ctl.bell_duration = state.bell_duration; | |
2226 XChangeKeyboardControl (display, KBBellPitch|KBBellDuration, &ctl); | |
2227 | |
2228 /* #### ungrab server? */ | |
2229 XSync (display, 0); | |
2230 } | |
2231 } | |
2232 | |
2233 | |
2234 /************************************************************************/ | |
2235 /* initialization */ | |
2236 /************************************************************************/ | |
2237 | |
2238 void | |
2239 console_type_create_redisplay_x (void) | |
2240 { | |
2241 /* redisplay methods */ | |
2242 CONSOLE_HAS_METHOD (x, text_width); | |
2243 CONSOLE_HAS_METHOD (x, output_display_block); | |
2244 CONSOLE_HAS_METHOD (x, divider_width); | |
2245 CONSOLE_HAS_METHOD (x, divider_height); | |
2246 CONSOLE_HAS_METHOD (x, eol_cursor_width); | |
2247 CONSOLE_HAS_METHOD (x, output_vertical_divider); | |
2248 CONSOLE_HAS_METHOD (x, clear_to_window_end); | |
2249 CONSOLE_HAS_METHOD (x, clear_region); | |
2250 CONSOLE_HAS_METHOD (x, clear_frame); | |
2251 CONSOLE_HAS_METHOD (x, output_begin); | |
2252 CONSOLE_HAS_METHOD (x, output_end); | |
2253 CONSOLE_HAS_METHOD (x, flash); | |
2254 CONSOLE_HAS_METHOD (x, ring_bell); | |
2255 } |