Merge branch '4560_extfs_open_deleted_archive'
[midnight-commander.git] / src / cons.handler.c
blob5f58a4271ec4618142778486980955333fcf7854
1 /*
2 Client interface for General purpose Linux console save/restore server
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file cons.handler.c
24 * \brief Source: client %interface for General purpose Linux console save/restore server
27 #include <config.h>
29 #include <stdlib.h>
30 #include <sys/wait.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #ifdef __FreeBSD__
35 #include <sys/consio.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
38 #endif
39 #endif
41 #include "lib/global.h"
43 #include "lib/unixcompat.h"
44 #include "lib/tty/tty.h"
45 #include "lib/tty/color.h" /* tty_set_normal_attrs */
46 #include "lib/tty/win.h"
47 #include "lib/util.h" /* mc_build_filename() */
49 #include "consaver/cons.saver.h"
51 /*** global variables ****************************************************************************/
53 #ifdef __linux__
54 int cons_saver_pid = 1;
55 #endif /* __linux__ */
57 /*** file scope macro definitions ****************************************************************/
59 #if defined(__FreeBSD__)
60 #define FD_OUT 1
61 #define cursor_to(x, y) \
62 do \
63 { \
64 printf("\x1B[%d;%df", (y) + 1, (x) + 1); \
65 fflush(stdout); \
66 } while (0)
67 #endif /* __linux__ */
69 /*** file scope type declarations ****************************************************************/
71 /*** forward declarations (file scope functions) *************************************************/
73 /*** file scope variables ************************************************************************/
75 #ifdef __linux__
76 /* The cons saver can't have a pid of 1, used to prevent bunches of
77 * #ifdef linux */
78 static int pipefd1[2] = { -1, -1 };
79 static int pipefd2[2] = { -1, -1 };
80 #elif defined(__FreeBSD__)
81 static struct scrshot screen_shot;
82 static struct vid_info screen_info;
83 #endif /* __linux__ */
85 /* --------------------------------------------------------------------------------------------- */
86 /*** file scope functions ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
89 #ifdef __linux__
90 static void
91 show_console_contents_linux (int starty, unsigned char begin_line, unsigned char end_line)
93 unsigned char message = 0;
94 unsigned short bytes = 0;
95 int i;
96 ssize_t ret;
98 /* Is tty console? */
99 if (mc_global.tty.console_flag == '\0')
100 return;
101 /* Paranoid: Is the cons.saver still running? */
102 if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT))
104 cons_saver_pid = 0;
105 mc_global.tty.console_flag = '\0';
106 return;
109 /* Send command to the console handler */
110 message = CONSOLE_CONTENTS;
111 ret = write (pipefd1[1], &message, 1);
112 /* Check for outdated cons.saver */
113 ret = read (pipefd2[0], &message, 1);
114 if (message != CONSOLE_CONTENTS)
115 return;
117 /* Send the range of lines that we want */
118 ret = write (pipefd1[1], &begin_line, 1);
119 ret = write (pipefd1[1], &end_line, 1);
120 /* Read the corresponding number of bytes */
121 ret = read (pipefd2[0], &bytes, 2);
123 /* Read the bytes and output them */
124 for (i = 0; i < bytes; i++)
126 if ((i % COLS) == 0)
127 tty_gotoyx (starty + (i / COLS), 0);
128 ret = read (pipefd2[0], &message, 1);
129 tty_print_char (message);
132 /* Read the value of the mc_global.tty.console_flag */
133 ret = read (pipefd2[0], &message, 1);
134 (void) ret;
137 /* --------------------------------------------------------------------------------------------- */
139 static void
140 handle_console_linux (console_action_t action)
142 int status;
144 switch (action)
146 case CONSOLE_INIT:
147 /* Close old pipe ends in case it is the 2nd time we run cons.saver */
148 status = close (pipefd1[1]);
149 status = close (pipefd2[0]);
150 /* Create two pipes for communication */
151 if (!((pipe (pipefd1) == 0) && ((pipe (pipefd2)) == 0)))
153 mc_global.tty.console_flag = '\0';
154 break;
156 /* Get the console saver running */
157 cons_saver_pid = fork ();
158 if (cons_saver_pid < 0)
160 /* Cannot fork */
161 /* Delete pipes */
162 status = close (pipefd1[1]);
163 status = close (pipefd1[0]);
164 status = close (pipefd2[1]);
165 status = close (pipefd2[0]);
166 mc_global.tty.console_flag = '\0';
168 else if (cons_saver_pid > 0)
170 /* Parent */
171 /* Close the extra pipe ends */
172 status = close (pipefd1[0]);
173 status = close (pipefd2[1]);
174 /* Was the child successful? */
175 status = read (pipefd2[0], &mc_global.tty.console_flag, 1);
176 if (mc_global.tty.console_flag == '\0')
178 pid_t ret;
179 status = close (pipefd1[1]);
180 status = close (pipefd2[0]);
181 ret = waitpid (cons_saver_pid, &status, 0);
182 (void) ret;
185 else
187 /* Child */
188 char *tty_name;
190 /* Close the extra pipe ends */
191 status = close (pipefd1[1]);
192 status = close (pipefd2[0]);
193 tty_name = ttyname (0);
194 /* Bind the pipe 0 to the standard input */
197 gboolean ok;
199 if (dup2 (pipefd1[0], STDIN_FILENO) == -1)
200 break;
201 status = close (pipefd1[0]);
202 /* Bind the pipe 1 to the standard output */
203 if (dup2 (pipefd2[1], STDOUT_FILENO) == -1)
204 break;
206 status = close (pipefd2[1]);
207 /* Bind standard error to /dev/null */
208 status = open ("/dev/null", O_WRONLY);
209 if (status == -1)
210 break;
211 ok = dup2 (status, STDERR_FILENO) != -1;
212 status = close (status);
213 if (!ok)
214 break;
216 if (tty_name != NULL)
218 char *mc_conssaver;
220 /* Exec the console save/restore handler */
221 mc_conssaver = mc_build_filename (SAVERDIR, "cons.saver", (char *) NULL);
222 execl (mc_conssaver, "cons.saver", tty_name, (char *) NULL);
224 /* Console is not a tty or execl() failed */
226 while (0);
227 mc_global.tty.console_flag = '\0';
228 status = write (1, &mc_global.tty.console_flag, 1);
229 my_exit (3);
230 } /* if (cons_saver_pid ...) */
231 break;
233 case CONSOLE_DONE:
234 case CONSOLE_SAVE:
235 case CONSOLE_RESTORE:
236 /* Is tty console? */
237 if (mc_global.tty.console_flag == '\0')
238 return;
239 /* Paranoid: Is the cons.saver still running? */
240 if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT))
242 cons_saver_pid = 0;
243 mc_global.tty.console_flag = '\0';
244 return;
246 /* Send command to the console handler */
248 /* Convert enum (i.e. int) to char to write the correct value
249 * (the least byte) regardless of machine endianness. */
250 char act = (char) action;
252 status = write (pipefd1[1], &act, 1);
254 if (action != CONSOLE_DONE)
256 /* Wait the console handler to do its job */
257 status = read (pipefd2[0], &mc_global.tty.console_flag, 1);
259 if (action == CONSOLE_DONE || mc_global.tty.console_flag == '\0')
261 /* We are done -> Let's clean up */
262 pid_t ret;
263 close (pipefd1[1]);
264 close (pipefd2[0]);
265 ret = waitpid (cons_saver_pid, &status, 0);
266 (void) ret;
267 mc_global.tty.console_flag = '\0';
269 break;
270 default:
271 break;
275 #elif defined(__FreeBSD__)
277 /* --------------------------------------------------------------------------------------------- */
279 * FreeBSD support copyright (C) 2003 Alexander Serkov <serkov@ukrpost.net>.
280 * Support for screenmaps by Max Khon <fjoe@FreeBSD.org>
283 static void
284 console_init (void)
286 if (mc_global.tty.console_flag != '\0')
287 return;
289 screen_info.size = sizeof (screen_info);
290 if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1)
291 return;
293 memset (&screen_shot, 0, sizeof (screen_shot));
294 screen_shot.xsize = screen_info.mv_csz;
295 screen_shot.ysize = screen_info.mv_rsz;
296 screen_shot.buf = g_try_malloc (screen_info.mv_csz * screen_info.mv_rsz * 2);
297 if (screen_shot.buf != NULL)
298 mc_global.tty.console_flag = '\001';
301 /* --------------------------------------------------------------------------------------------- */
303 static void
304 set_attr (unsigned attr)
307 * Convert color indices returned by SCRSHOT (red=4, green=2, blue=1)
308 * to indices for ANSI sequences (red=1, green=2, blue=4).
310 static const int color_map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
311 int bc, tc;
313 tc = attr & 0xF;
314 bc = (attr >> 4) & 0xF;
316 printf ("\x1B[%d;%d;3%d;4%dm", (bc & 8) ? 5 : 25, (tc & 8) ? 1 : 22,
317 color_map[tc & 7], color_map[bc & 7]);
320 /* --------------------------------------------------------------------------------------------- */
322 static void
323 console_restore (void)
325 int i, last;
327 if (mc_global.tty.console_flag == '\0')
328 return;
330 cursor_to (0, 0);
332 /* restoring all content up to cursor position */
333 last = screen_info.mv_row * screen_info.mv_csz + screen_info.mv_col;
334 for (i = 0; i < last; ++i)
336 set_attr ((screen_shot.buf[i] >> 8) & 0xFF);
337 putc (screen_shot.buf[i] & 0xFF, stdout);
340 /* restoring cursor color */
341 set_attr ((screen_shot.buf[last] >> 8) & 0xFF);
343 fflush (stdout);
346 /* --------------------------------------------------------------------------------------------- */
348 static void
349 console_shutdown (void)
351 if (mc_global.tty.console_flag == '\0')
352 return;
354 g_free (screen_shot.buf);
356 mc_global.tty.console_flag = '\0';
359 /* --------------------------------------------------------------------------------------------- */
361 static void
362 console_save (void)
364 int i;
365 scrmap_t map;
366 scrmap_t revmap;
368 if (mc_global.tty.console_flag == '\0')
369 return;
371 /* screen_info.size is already set in console_init() */
372 if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1)
374 console_shutdown ();
375 return;
378 /* handle console resize */
379 if (screen_info.mv_csz != screen_shot.xsize || screen_info.mv_rsz != screen_shot.ysize)
381 console_shutdown ();
382 console_init ();
385 if (ioctl (FD_OUT, CONS_SCRSHOT, &screen_shot) == -1)
387 console_shutdown ();
388 return;
391 if (ioctl (FD_OUT, GIO_SCRNMAP, &map) == -1)
393 console_shutdown ();
394 return;
397 for (i = 0; i < 256; i++)
399 char *p = memchr (map.scrmap, i, 256);
400 revmap.scrmap[i] = p ? p - map.scrmap : i;
403 for (i = 0; i < screen_shot.xsize * screen_shot.ysize; i++)
405 /* *INDENT-OFF* */
406 screen_shot.buf[i] = (screen_shot.buf[i] & 0xff00)
407 | (unsigned char) revmap.scrmap[screen_shot.buf[i] & 0xff];
408 /* *INDENT-ON* */
412 /* --------------------------------------------------------------------------------------------- */
414 static void
415 show_console_contents_freebsd (int starty, unsigned char begin_line, unsigned char end_line)
417 int col, line;
418 char c;
420 if (mc_global.tty.console_flag == '\0')
421 return;
423 for (line = begin_line; line <= end_line; line++)
425 tty_gotoyx (starty + line - begin_line, 0);
426 for (col = 0; col < MIN (COLS, screen_info.mv_csz); col++)
428 c = screen_shot.buf[line * screen_info.mv_csz + col] & 0xFF;
429 tty_print_char (c);
434 /* --------------------------------------------------------------------------------------------- */
436 static void
437 handle_console_freebsd (console_action_t action)
439 switch (action)
441 case CONSOLE_INIT:
442 console_init ();
443 break;
445 case CONSOLE_DONE:
446 console_shutdown ();
447 break;
449 case CONSOLE_SAVE:
450 console_save ();
451 break;
453 case CONSOLE_RESTORE:
454 console_restore ();
455 break;
456 default:
457 break;
460 #endif /* __FreeBSD__ */
462 /* --------------------------------------------------------------------------------------------- */
463 /*** public functions ****************************************************************************/
464 /* --------------------------------------------------------------------------------------------- */
466 void
467 show_console_contents (int starty, unsigned char begin_line, unsigned char end_line)
469 tty_set_normal_attrs ();
471 if (look_for_rxvt_extensions ())
473 show_rxvt_contents (starty, begin_line, end_line);
474 return;
476 #ifdef __linux__
477 show_console_contents_linux (starty, begin_line, end_line);
478 #elif defined (__FreeBSD__)
479 show_console_contents_freebsd (starty, begin_line, end_line);
480 #else
481 mc_global.tty.console_flag = '\0';
482 #endif
485 /* --------------------------------------------------------------------------------------------- */
487 void
488 handle_console (console_action_t action)
490 (void) action;
492 if (look_for_rxvt_extensions ())
493 return;
495 #ifdef __linux__
496 handle_console_linux (action);
497 #elif defined (__FreeBSD__)
498 handle_console_freebsd (action);
499 #endif
502 /* --------------------------------------------------------------------------------------------- */