2 * Mike Gerwitz (mtg@gnu.org)
4 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 2008, 2009
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Micah Cowan (micah@cowan.name)
10 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
11 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
12 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
13 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
14 * Copyright (c) 1987 Oliver Laumann
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3, or (at your option)
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program (see the file COPYING); if not, see
28 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
29 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
31 ****************************************************************
47 /* TODO: rid global variable (has been renamed to point this out; see commit
53 /* maximum limit on MakeWinMsgEv recursion */
54 #define WINMSG_RECLIMIT 10
56 /* escape char for backtick output */
57 #define WINMSG_BT_ESC '\005'
59 /* redundant definition abstraction for escape character handlers; note that
60 * a variable varadic macro name is a gcc extension and is not portable, so
61 * we instead use two separate macros */
62 #define WINMSG_ESC_PARAMS \
63 __attribute__((unused)) WinMsgEsc *esc, \
64 __attribute__((unused)) char **src, \
65 __attribute__((unused)) WinMsgBufContext *wmbc, \
66 __attribute__((unused)) WinMsgCond *cond
67 #define winmsg_esc__name(name) __WinMsgEsc##name
68 #define winmsg_esc__def(name) static void winmsg_esc__name(name)
69 #define winmsg_esc(name) winmsg_esc__def(name)(WINMSG_ESC_PARAMS)
70 #define winmsg_esc_ex(name, ...) winmsg_esc__def(name)(WINMSG_ESC_PARAMS, __VA_ARGS__)
71 #define WINMSG_ESC_ARGS &esc, &s, wmbc, cond
72 #define WinMsgDoEsc(name) winmsg_esc__name(name)(WINMSG_ESC_ARGS)
73 #define WinMsgDoEscEx(name, ...) winmsg_esc__name(name)(WINMSG_ESC_ARGS, __VA_ARGS__)
75 static void _MakeWinMsgEvRec(WinMsgBufContext
*, WinMsgCond
*, char *, Window
*, int *, int);
78 /* TODO: remove the redundant arguments */
79 static char *pad_expand(WinMsgBuf
*winmsg
, char *buf
, char *p
, int numpad
, int padlen
)
84 padlen
= padlen
- (p
- buf
); /* space for rent */
87 pn2
= pn
= p
+ padlen
;
90 if (r
&& *p
!= CHRPAD
&& p
- buf
== winmsg
->rendpos
[r
- 1]) {
91 winmsg
->rendpos
[--r
] = pn
- buf
;
97 i
= numpad
> 0 ? (padlen
+ numpad
- 1) / numpad
: 0;
102 if (r
&& p
- buf
== winmsg
->rendpos
[r
- 1])
103 winmsg
->rendpos
[--r
] = pn
- buf
;
109 int AddWinMsgRend(WinMsgBuf
*winmsg
, const char *str
, uint64_t r
)
111 if (winmsg
->numrend
>= MAX_WINMSG_REND
|| str
< winmsg
->buf
|| str
>= winmsg
->buf
+ MAXSTR
)
114 wmb_rendadd(winmsg
, r
, str
- winmsg
->buf
);
119 winmsg_esc_ex(Wflags
, Window
*win
)
124 AddWindowFlags(wmbc
->p
, wmbc_bytesleft(wmbc
), win
);
134 wmbc_printf(wmbc
, "%d", (esc
->flags
.plus
&& display
) ? D_userpid
: getpid());
137 winmsg_esc_ex(Backtick
, int id
, Window
*win
, int *tick
, struct timeval
*now
, int rec
)
142 if (!(bt
= bt_find_id(id
)))
145 /* TODO: not re-entrant; static buffer returned */
146 btresult
= runbacktick(bt
, tick
, now
->tv_sec
);
147 _MakeWinMsgEvRec(wmbc
, cond
, btresult
, win
, tick
, rec
);
150 winmsg_esc_ex(CopyMode
, Event
*ev
)
152 if (display
&& ev
&& ev
!= &D_hstatusev
) { /* Hack */
153 /* Is the layer in the current canvas in copy mode? */
154 Canvas
*cv
= (Canvas
*)ev
->data
;
155 if (ev
== &cv
->c_captev
&& cv
->c_layer
->l_layfn
== &MarkLf
)
162 if (display
&& D_ESCseen
) {
167 winmsg_esc_ex(Focus
, Window
*win
, Event
*ev
)
169 /* small hack (TODO: explain.) */
170 if (display
&& ((ev
&& ev
== &D_forecv
->c_captev
) || (!ev
&& win
&& win
== D_fore
)))
171 esc
->flags
.minus
^= 1;
173 if (esc
->flags
.minus
)
179 if (*wmbc_strcpy(wmbc
, HostName
))
183 winmsg_esc_ex(Hstatus
, Window
*win
, int *tick
, int rec
)
185 if (!win
|| win
->w_hstatus
== NULL
|| *win
->w_hstatus
== '\0')
188 _MakeWinMsgEvRec(wmbc
, cond
, win
->w_hstatus
, win
, tick
, rec
);
191 winmsg_esc_ex(PadOrTrunc
, int *numpad
, int *lastpad
, int padlen
)
193 /* TODO: encapsulate */
194 WinMsgBuf
*winmsg
= wmbc
->buf
;
197 wmbc_putchar(wmbc
, ' ');
198 wmbc
->p
--; /* TODO: temporary to work with old code */
200 if (esc
->num
|| esc
->flags
.zero
|| esc
->flags
.plus
|| esc
->flags
.lng
|| (**src
!= WINESC_PAD
)) {
201 /* expand all pads */
202 if (esc
->flags
.minus
) {
203 esc
->num
= (esc
->flags
.plus
? *lastpad
: padlen
) - esc
->num
;
205 if (!esc
->flags
.plus
&& padlen
== 0)
206 esc
->num
= wmbc
->p
- winmsg
->buf
;
209 } else if (!esc
->flags
.zero
) {
210 if (**src
!= WINESC_PAD
&& esc
->num
== 0 && !esc
->flags
.plus
)
217 esc
->num
= wmbc
->p
- winmsg
->buf
;
219 esc
->num
= (padlen
- (esc
->flags
.plus
? *lastpad
: 0)) * esc
->num
/ 100;
226 esc
->num
+= *lastpad
;
228 if (esc
->num
> MAXSTR
- 1)
229 esc
->num
= MAXSTR
- 1;
232 wmbc
->p
= pad_expand(winmsg
, winmsg
->buf
, wmbc
->p
, *numpad
, esc
->num
);
235 if (wmbc
->p
- winmsg
->buf
> esc
->num
&& !esc
->flags
.lng
) {
238 if (wmbc
->trunc
.pos
== -1) {
239 wmbc
->trunc
.pos
= *lastpad
;
240 wmbc
->trunc
.perc
= 0;
243 trunc
= *lastpad
+ wmbc
->trunc
.perc
* (esc
->num
- *lastpad
) / 100;
244 if (trunc
> esc
->num
)
246 if (trunc
< *lastpad
)
249 left
= wmbc
->trunc
.pos
- trunc
;
250 if (left
> wmbc
->p
- winmsg
->buf
- esc
->num
)
251 left
= wmbc
->p
- winmsg
->buf
- esc
->num
;
254 if (left
+ *lastpad
> wmbc
->p
- winmsg
->buf
)
255 left
= wmbc
->p
- winmsg
->buf
- *lastpad
;
257 if (wmbc
->p
- winmsg
->buf
- *lastpad
- left
> 0)
258 memmove(winmsg
->buf
+ *lastpad
, winmsg
->buf
+ *lastpad
+ left
,
259 wmbc
->p
- winmsg
->buf
- *lastpad
- left
);
263 while (r
&& winmsg
->rendpos
[r
- 1] > *lastpad
) {
265 winmsg
->rendpos
[r
] -= left
;
266 if (winmsg
->rendpos
[r
] < *lastpad
)
267 winmsg
->rendpos
[r
] = *lastpad
;
270 if (wmbc
->trunc
.ellip
) {
271 if (wmbc
->p
- winmsg
->buf
> *lastpad
)
272 winmsg
->buf
[*lastpad
] = '.';
273 if (wmbc
->p
- winmsg
->buf
> *lastpad
+ 1)
274 winmsg
->buf
[*lastpad
+ 1] = '.';
275 if (wmbc
->p
- winmsg
->buf
> *lastpad
+ 2)
276 winmsg
->buf
[*lastpad
+ 2] = '.';
280 if (wmbc
->p
- winmsg
->buf
> esc
->num
) {
281 wmbc
->p
= winmsg
->buf
+ esc
->num
;
282 if (wmbc
->trunc
.ellip
) {
283 if (esc
->num
- 1 >= *lastpad
)
285 if (esc
->num
- 2 >= *lastpad
)
287 if (esc
->num
- 3 >= *lastpad
)
292 while (r
&& winmsg
->rendpos
[r
- 1] > esc
->num
)
293 winmsg
->rendpos
[--r
] = esc
->num
;
296 wmbc
->trunc
.pos
= -1;
297 wmbc
->trunc
.ellip
= false;
299 if (*lastpad
> wmbc
->p
- winmsg
->buf
)
300 *lastpad
= wmbc
->p
- winmsg
->buf
;
303 if (**src
== WINESC_PAD
) {
304 while (wmbc
->p
- winmsg
->buf
< esc
->num
)
305 wmbc_putchar(wmbc
, ' ');
307 *lastpad
= wmbc
->p
- winmsg
->buf
;
308 wmbc
->trunc
.pos
= -1;
309 wmbc
->trunc
.ellip
= false;
312 *wmbc
->p
= CHRPAD
; /* internal pad representation */
316 wmbc
->p
++; /* TODO: temporary; see above */
320 * Processes rendition
322 * The first character of SRC is assumed to be (unverified) the opening brace
327 char rbuf
[RENDBUF_SIZE
];
332 for (i
= 0; i
< (RENDBUF_SIZE
-1); i
++) {
334 if (c
&& c
!= WINESC_REND_END
)
340 if (((*src
)[i
] == WINESC_REND_END
) && (wmbc
->buf
->numrend
< MAX_WINMSG_REND
)) {
343 if (i
!= 1 || rbuf
[0] != WINESC_REND_POP
)
344 r
= ParseAttrColor(rbuf
, 0);
345 AddWinMsgRend(wmbc
->buf
, wmbc
->p
, r
);
352 char *session_name
= strchr(SocketName
, '.') + 1;
354 if (*wmbc_strcpy(wmbc
, session_name
))
358 winmsg_esc_ex(TruncPos
, uint8_t perc
, bool ellip
)
360 /* TODO: encapsulate */
361 wmbc
->trunc
.pos
= wmbc_offset(wmbc
);
362 wmbc
->trunc
.perc
= perc
;
363 wmbc
->trunc
.ellip
= ellip
;
366 winmsg_esc_ex(WinNames
, const bool hide_cur
, Window
*win
)
368 Window
*oldfore
= NULL
;
369 size_t max
= wmbc_bytesleft(wmbc
);
376 /* TODO: no need to enforce a limit here */
377 AddWindows(wmbc
, max
- 1,
379 | (esc
->flags
.lng
? 0 : 2)
380 | (esc
->flags
.plus
? 4 : 0)
381 | (esc
->flags
.minus
? 8 : 0),
382 win
? win
->w_number
: -1);
393 winmsg_esc_ex(WinArgv
, Window
*win
)
395 if (!win
|| !win
->w_cmdargs
[0])
398 wmbc_printf(wmbc
, "%s", win
->w_cmdargs
[0]);
401 if (**src
== WINESC_CMD_ARGS
) {
402 for (int i
= 1; win
->w_cmdargs
[i
]; i
++) {
403 wmbc_printf(wmbc
, " %s", win
->w_cmdargs
[i
]);
409 winmsg_esc_ex(WinNum
, Window
*win
)
415 wmbc_printf(wmbc
, "%*s", esc
->num
, esc
->num
> 1 ? "--" : "-");
417 wmbc_printf(wmbc
, "%*d", esc
->num
, win
->w_number
);
423 winmsg_esc_ex(WinCount
, Window
*win
)
429 wmbc_printf(wmbc
, "%*s", esc
->num
, esc
->num
> 1 ? "--" : "-");
433 for (Window
*w
= win
; w
; w
= w
->w_prev_mru
) {
434 if (esc
->flags
.minus
) {
435 if (w
->w_group
== win
->w_group
&& w
->w_type
!= W_TYPE_GROUP
) {
443 wmbc_printf(wmbc
, "%*d", esc
->num
, count
);
449 winmsg_esc_ex(WinLogName
, Window
*win
)
451 if (win
&& win
->w_log
&& win
->w_log
->fp
)
452 wmbc_printf(wmbc
, "%s", win
->w_log
->name
);
456 winmsg_esc_ex(WinTty
, Window
*win
)
458 if (win
&& win
->w_tty
[0])
459 wmbc_printf(wmbc
, "%s", win
->w_tty
);
463 winmsg_esc_ex(WinSize
, Window
*win
)
466 wmbc_printf(wmbc
, "--x--");
468 wmbc_printf(wmbc
, "%dx%d", win
->w_width
, win
->w_height
);
471 winmsg_esc_ex(WinTitle
, Window
*win
)
476 if (*wmbc_strcpy(wmbc
, win
->w_title
))
480 winmsg_esc_ex(WinGroup
, Window
*win
)
482 if (!win
|| !win
->w_group
)
485 if (*wmbc_strcpy(wmbc
, win
->w_group
->w_title
))
490 winmsg_esc_ex(Cond
, int *condrend
)
492 if (wmc_is_active(cond
)) {
494 wmbc
->p
= wmbc
->buf
->buf
+ wmc_end(cond
, wmbc_offset(wmbc
), &chg
);
497 wmbc
->buf
->numrend
= *condrend
;
503 wmc_init(cond
, wmbc_offset(wmbc
));
504 *condrend
= wmbc
->buf
->numrend
;
507 winmsg_esc_ex(CondElse
, int *condrend
)
509 if (wmc_is_active(cond
)) {
511 wmbc
->p
= wmbc
->buf
->buf
+ wmc_else(cond
, wmbc_offset(wmbc
), &chg
);
513 /* if the true branch was discarded, restore to previous rendition
514 * state; otherwise, we're keeping it, so update the rendition state */
516 wmbc
->buf
->numrend
= *condrend
;
518 *condrend
= wmbc
->buf
->numrend
;
523 /* TODO: this is temporary until refactoring is complete and this code need not
525 static void _MakeWinMsgEvRec(WinMsgBufContext
*wmbc
, WinMsgCond
*cond
, char *str
,
526 Window
*win
, int *tick
, int rec
)
529 WinMsgBuf
*tmp
= wmb_create();
532 Panic(0, "%s", strnomem
);
534 /* create message in a new buffer and merge into our own */
535 MakeWinMsgEv(tmp
, str
, win
, WINMSG_BT_ESC
, 0, NULL
, rec
+ 1);
536 if (*wmbc_mergewmb(wmbc
, tmp
))
539 /* TODO: handle some other way; not re-entrant */
540 if (!*tick
|| oldtick
< *tick
)
546 /* TODO: const char *str for safety and reassurance */
547 char *MakeWinMsgEv(WinMsgBuf
*winmsg
, char *str
, Window
*win
,
548 int chesc
, int padlen
, Event
*ev
, int rec
)
555 WinMsgBufContext
*wmbc
;
559 /* TODO: temporary to work into existing code */
560 if (winmsg
== NULL
) {
561 if (g_winmsg
== NULL
) {
562 if ((g_winmsg
= wmb_create()) == NULL
)
563 Panic(0, "%s", strnomem
);
568 if (rec
> WINMSG_RECLIMIT
)
571 cond
= calloc(1, sizeof(WinMsgCond
));
573 Panic(0, "%s", strnomem
);
575 /* set to sane state (clear garbage) */
578 /* TODO: we can get rid of this once winmsg is properly handled by caller */
579 if (winmsg
->numrend
> 0)
583 wmbc
= wmbc_create(winmsg
);
586 Panic(0, "%s", strnomem
);
589 gettimeofday(&now
, NULL
);
590 for (char *s
= str
; *s
; s
++) {
592 if ((chesc
== '%') && (*s
== '^')) {
594 if (*s
!= '^' && *s
>= 64)
595 wmbc_putchar(wmbc
, *s
& 0x1f);
598 wmbc_putchar(wmbc
, *s
);
602 if (*++s
== chesc
) /* double escape ? */
605 /* initialize escape */
606 if ((esc
.flags
.plus
= (*s
== '+')) != 0)
608 if ((esc
.flags
.minus
= (*s
== '-')) != 0)
610 if ((esc
.flags
.zero
= (*s
== '0')) != 0)
613 while (*s
>= '0' && *s
<= '9')
614 esc
.num
= esc
.num
* 10 + (*s
++ - '0');
615 if ((esc
.flags
.lng
= (*s
== 'L')) != 0)
620 WinMsgDoEscEx(Cond
, &qmnumrend
);
622 case WINESC_COND_ELSE
:
623 WinMsgDoEscEx(CondElse
, &qmnumrend
);
626 WinMsgDoEscEx(Hstatus
, win
, &tick
, rec
);
628 case WINESC_BACKTICK
:
629 WinMsgDoEscEx(Backtick
, esc
.num
, win
, &tick
, &now
, rec
);
632 case WINESC_CMD_ARGS
:
633 WinMsgDoEscEx(WinArgv
, win
);
635 case WINESC_WIN_NAMES
:
636 case WINESC_WIN_NAMES_NOCUR
:
637 WinMsgDoEscEx(WinNames
, (*s
== WINESC_WIN_NAMES_NOCUR
), win
);
640 WinMsgDoEscEx(Wflags
, win
);
642 case WINESC_WIN_TITLE
:
643 WinMsgDoEscEx(WinTitle
, win
);
645 case WINESC_WIN_GROUP
:
646 WinMsgDoEscEx(WinGroup
, win
);
648 case WINESC_REND_START
:
652 WinMsgDoEsc(HostName
);
654 case WINESC_SESS_NAME
:
655 WinMsgDoEsc(SessName
);
661 WinMsgDoEscEx(Focus
, win
, ev
);
663 case WINESC_COPY_MODE
:
664 WinMsgDoEscEx(CopyMode
, ev
);
666 case WINESC_ESC_SEEN
:
667 WinMsgDoEsc(EscSeen
);
669 case WINESC_TRUNC_POS
:
670 WinMsgDoEscEx(TruncPos
,
671 ((esc
.num
> 100) ? 100 : esc
.num
),
676 WinMsgDoEscEx(PadOrTrunc
, &numpad
, &lastpad
, padlen
);
678 case WINESC_WIN_SIZE
:
679 WinMsgDoEscEx(WinSize
, win
);
682 WinMsgDoEscEx(WinNum
, win
);
684 case WINESC_WIN_COUNT
:
685 WinMsgDoEscEx(WinCount
, win
);
687 case WINESC_WIN_LOGNAME
:
688 WinMsgDoEscEx(WinLogName
, win
);
691 WinMsgDoEscEx(WinTty
, win
);
695 if (wmc_is_active(cond
) && !wmc_is_set(cond
))
696 wmbc
->p
= wmbc
->buf
->buf
+ wmc_end(cond
, wmbc_offset(wmbc
), NULL
) + 1;
697 wmbc_putchar(wmbc
, '\0' );
698 wmbc
->p
--; /* TODO: temporary to work with old code */
700 if (padlen
> MAXSTR
- 1)
702 pad_expand(winmsg
, winmsg
->buf
, wmbc
->p
, numpad
, padlen
);
705 evdeq(ev
); /* just in case */
709 now
.tv_usec
= 100000;
713 now
.tv_sec
+= tick
- (now
.tv_sec
% tick
);
714 ev
->timeout
= (now
.tv_sec
* 1000 + now
.tv_usec
/ 1000);
722 char *MakeWinMsg(char *s
, Window
*win
, int esc
)
724 return MakeWinMsgEv(NULL
, s
, win
, esc
, 0, NULL
, 0);
727 static int WindowChangedCheck(char *s
, WinMsgEscapeChar what
, int *hp
)
732 if (*s
++ != (hp
? '%' : '\005'))
737 while (*s
>= '0' && *s
<= '9')
743 if (*s
== WINESC_HSTATUS
)
745 if (*s
== (char)what
|| ((*s
| l
) == (int)what
))
755 void WindowChanged(Window
*win
, WinMsgEscapeChar what
)
757 int inwstr
, inhstr
, inlstr
;
758 int inwstrh
= 0, inhstrh
= 0, inlstrh
= 0;
760 Display
*olddisplay
= display
;
763 if (what
== WINESC_WFLAGS
) {
764 WindowChanged(NULL
, WINESC_WIN_NAMES
| 0x100);
765 WindowChanged(NULL
, WINESC_WIN_NAMES_NOCUR
| 0x100);
769 inwstr
= WindowChangedCheck(captionstring
, what
, &inwstrh
);
770 inhstr
= WindowChangedCheck(hstatusstring
, what
, &inhstrh
);
771 inlstr
= WindowChangedCheck(wliststr
, what
, &inlstrh
);
778 for (display
= displays
; display
; display
= display
->d_next
) {
781 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
) {
783 || (inlstrh
&& win
&& win
->w_hstatus
&& *win
->w_hstatus
784 && WindowChangedCheck(win
->w_hstatus
, what
, NULL
)))
785 WListUpdatecv(cv
, NULL
);
786 win
= Layer2Window(cv
->c_layer
);
788 || (inwstrh
&& win
&& win
->w_hstatus
&& *win
->w_hstatus
789 && WindowChangedCheck(win
->w_hstatus
, what
, NULL
))) {
791 if (cv
->c_ys
- 1 >= 0)
792 RefreshLine(cv
->c_ys
- 1, 0, D_width
-1 , 0);
794 if (cv
->c_ye
+ 1 < D_height
)
795 RefreshLine(cv
->c_ye
+ 1, 0, D_width
- 1, 0);
801 || (inhstrh
&& win
&& win
->w_hstatus
&& *win
->w_hstatus
802 && WindowChangedCheck(win
->w_hstatus
, what
, NULL
)))
804 if (ox
!= -1 && oy
!= -1)
807 display
= olddisplay
;
811 if (win
->w_hstatus
&& *win
->w_hstatus
&& (inwstrh
|| inhstrh
|| inlstrh
)
812 && WindowChangedCheck(win
->w_hstatus
, what
, NULL
)) {
817 if (!inwstr
&& !inhstr
&& !inlstr
)
819 for (display
= displays
; display
; display
= display
->d_next
) {
823 for (cv
= D_cvlist
; cv
; cv
= cv
->c_next
) {
825 WListUpdatecv(cv
, win
);
826 if (Layer2Window(cv
->c_layer
) != win
)
831 if (cv
->c_ys
-1 >= 0)
832 RefreshLine(cv
->c_ys
- 1, 0, D_width
- 1, 0);
834 if (cv
->c_ye
+ 1 < D_height
)
835 RefreshLine(cv
->c_ye
+ 1, 0, D_width
- 1, 0);
839 if (got
&& inhstr
&& win
== D_fore
)
841 if (ox
!= -1 && oy
!= -1)
844 display
= olddisplay
;