]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
[libplybootsplash] Add terminal class
authorRay Strode <rstrode@redhat.com>
Wed, 16 Sep 2009 20:57:13 +0000 (16:57 -0400)
committerRay Strode <rstrode@redhat.com>
Mon, 28 Sep 2009 15:23:36 +0000 (11:23 -0400)
This is the terminal settings portion of the window class.  With this
commit and subsequent commits to split out the higher level text output,
and graphical output portions, we should be able to drop window
in favor of the new display classes.

src/libplybootsplash/Makefile.am
src/libplybootsplash/ply-terminal.c [new file with mode: 0644]
src/libplybootsplash/ply-terminal.h [new file with mode: 0644]

index 51b9b842d7cca8971c72da77d028ebe46a27a70e..adf75f1c13ad4f7c59d0aa5d3c7572e5089adcb5 100644 (file)
@@ -18,6 +18,7 @@ libplybootsplash_HEADERS = \
                    ply-pixel-buffer.h                                        \
                    ply-progress-animation.h                                  \
                    ply-progress-bar.h                                        \
+                   ply-terminal.h                                            \
                    ply-text-progress-bar.h                                   \
                    ply-throbber.h                                            \
                    ply-window.h
@@ -43,6 +44,7 @@ libplybootsplash_la_SOURCES = \
                    ply-animation.c                                          \
                    ply-progress-animation.c                                 \
                    ply-text-progress-bar.c                                  \
+                   ply-terminal.c                                           \
                    ply-pixel-buffer.c                                       \
                    ply-window.c                                             \
                    ply-boot-splash.c
diff --git a/src/libplybootsplash/ply-terminal.c b/src/libplybootsplash/ply-terminal.c
new file mode 100644 (file)
index 0000000..41eab54
--- /dev/null
@@ -0,0 +1,513 @@
+/* ply-terminal.c - APIs for terminaling text
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+#include "config.h"
+#include "ply-terminal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#include <linux/kd.h>
+#include <linux/major.h>
+#include <linux/vt.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+#include "ply-list.h"
+#include "ply-logger.h"
+#include "ply-utils.h"
+
+#ifndef TEXT_PALETTE_SIZE
+#define TEXT_PALETTE_SIZE 48
+#endif
+
+struct _ply_terminal
+{
+  ply_event_loop_t *loop;
+
+  struct termios original_term_attributes;
+
+  char *name;
+  int   fd;
+  int   vt_number;
+
+  ply_fd_watch_t *fd_watch;
+  ply_terminal_color_t foreground_color;
+  ply_terminal_color_t background_color;
+
+  uint8_t original_color_palette[TEXT_PALETTE_SIZE];
+  uint8_t color_palette[TEXT_PALETTE_SIZE];
+
+  int number_of_rows;
+  int number_of_columns;
+
+  uint32_t original_term_attributes_saved : 1;
+  uint32_t supports_text_color : 1;
+  uint32_t is_open : 1;
+};
+
+static bool ply_terminal_open_device (ply_terminal_t *terminal);
+
+ply_terminal_t *
+ply_terminal_new (const char *device_name)
+{
+  ply_terminal_t *terminal;
+
+  terminal = calloc (1, sizeof (ply_terminal_t));
+
+  terminal->loop = ply_event_loop_get_default ();
+  if (device_name != NULL)
+    {
+      if (strncmp (device_name, "/dev/", strlen ("/dev/")) == 0)
+        terminal->name = strdup (device_name);
+      else
+        asprintf (&terminal->name, "/dev/%s", device_name);
+    }
+  terminal->fd = -1;
+  terminal->vt_number = -1;
+
+  return terminal;
+}
+
+static void
+ply_terminal_look_up_color_palette (ply_terminal_t *terminal)
+{
+  if (ioctl (terminal->fd, GIO_CMAP, terminal->color_palette) < 0)
+    terminal->supports_text_color = false;
+  else
+    terminal->supports_text_color = true;
+}
+
+static bool
+ply_terminal_change_color_palette (ply_terminal_t *terminal)
+{
+  if (!terminal->supports_text_color)
+    return true;
+
+  if (ioctl (terminal->fd, PIO_CMAP, terminal->color_palette) < 0)
+    return false;
+
+  return true;
+}
+
+static void
+ply_terminal_save_color_palette (ply_terminal_t *terminal)
+{
+  if (!terminal->supports_text_color)
+    return;
+
+  memcpy (terminal->original_color_palette, terminal->color_palette,
+          TEXT_PALETTE_SIZE);
+}
+
+static void
+ply_terminal_restore_color_palette (ply_terminal_t *terminal)
+{
+  if (!terminal->supports_text_color)
+    return;
+
+  memcpy (terminal->color_palette, terminal->original_color_palette,
+          TEXT_PALETTE_SIZE);
+
+  ply_terminal_change_color_palette (terminal);
+}
+
+void
+ply_terminal_reset_colors (ply_terminal_t *terminal)
+{
+  assert (terminal != NULL);
+
+  ply_terminal_restore_color_palette (terminal);
+}
+
+bool
+ply_terminal_set_unbuffered_input (ply_terminal_t *terminal)
+{
+  struct termios term_attributes;
+
+  tcgetattr (terminal->fd, &term_attributes);
+
+  if (!terminal->original_term_attributes_saved)
+    {
+      terminal->original_term_attributes = term_attributes;
+      terminal->original_term_attributes_saved = true;
+    }
+
+  cfmakeraw (&term_attributes);
+
+  /* Make \n return go to the beginning of the next line */
+  term_attributes.c_oflag |= ONLCR;
+
+  if (tcsetattr (terminal->fd, TCSAFLUSH, &term_attributes) != 0)
+    return false;
+
+  return true;
+}
+
+bool
+ply_terminal_set_buffered_input (ply_terminal_t *terminal)
+{
+  struct termios term_attributes;
+
+  tcgetattr (terminal->fd, &term_attributes);
+
+  /* If someone already messed with the terminal settings,
+   * and they seem good enough, bail
+   */
+  if (term_attributes.c_lflag & ICANON)
+    return true;
+
+  /* If we don't know the original term attributes, or they were originally sucky,
+   * then invent some that are probably good enough.
+   */
+  if (!terminal->original_term_attributes_saved || !(terminal->original_term_attributes.c_lflag & ICANON))
+    {
+      term_attributes.c_iflag |= IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON;
+      term_attributes.c_oflag |= OPOST;
+      term_attributes.c_lflag |= ECHO | ECHONL | ICANON | ISIG | IEXTEN;
+
+      if (tcsetattr (terminal->fd, TCSAFLUSH, &term_attributes) != 0)
+        return false;
+
+      return true;
+    }
+
+  if (tcsetattr (terminal->fd, TCSAFLUSH, &terminal->original_term_attributes) != 0)
+    return false;
+
+  return true;
+}
+
+void
+ply_terminal_write (ply_terminal_t *terminal,
+                    const char     *format,
+                    ...)
+{
+  va_list args;
+  char *string;
+
+  assert (terminal != NULL);
+  assert (format != NULL);
+
+  string = NULL;
+  va_start (args, format);
+  vasprintf (&string, format, args);
+  va_end (args);
+
+  write (terminal->fd, string, strlen (string));
+  free (string);
+}
+
+static void
+on_tty_disconnected (ply_terminal_t *terminal)
+{
+  ply_trace ("tty disconnected (fd %d)", terminal->fd);
+  terminal->fd_watch = NULL;
+  terminal->fd = -1;
+
+  if (terminal->name != NULL)
+    {
+      ply_trace ("trying to reopen terminal '%s'", terminal->name);
+      ply_terminal_open_device (terminal);
+    }
+}
+
+static int
+get_active_vt (void)
+{
+  int console_fd;
+  struct vt_stat console_state = { 0 };
+
+  console_fd = open ("/dev/tty0", O_RDONLY | O_NOCTTY);
+
+  if (console_fd < 0)
+    goto out;
+
+  if (ioctl (console_fd, VT_GETSTATE, &console_state) < 0)
+    goto out;
+
+out:
+  if (console_fd >= 0)
+    close (console_fd);
+
+  return console_state.v_active;
+}
+
+static bool
+ply_terminal_look_up_geometry (ply_terminal_t *terminal)
+{
+    struct winsize terminal_size;
+
+    ply_trace ("looking up terminal text geometry");
+
+    if (ioctl (terminal->fd, TIOCGWINSZ, &terminal_size) < 0)
+      {
+        ply_trace ("could not read terminal text geometry: %m");
+        terminal->number_of_columns = 80;
+        terminal->number_of_rows = 24;
+        return false;
+      }
+
+    terminal->number_of_rows = terminal_size.ws_row;
+    terminal->number_of_columns = terminal_size.ws_col;
+
+    ply_trace ("terminal is now %dx%d text cells",
+               terminal->number_of_columns,
+               terminal->number_of_rows);
+
+    return true;
+}
+
+static void
+ply_terminal_check_for_vt (ply_terminal_t *terminal)
+{
+  int major_number, minor_number;
+  struct stat file_attributes;
+
+  assert (terminal != NULL);
+  assert (terminal->fd >= 0);
+
+  if (fstat (terminal->fd, &file_attributes) != 0)
+    return;
+
+  major_number = major (file_attributes.st_rdev);
+  minor_number = minor (file_attributes.st_rdev);
+
+  if (major_number == TTY_MAJOR)
+    terminal->vt_number = minor_number;
+  else
+    terminal->vt_number = -1;
+}
+
+static bool
+ply_terminal_open_device (ply_terminal_t *terminal)
+{
+  assert (terminal != NULL);
+  assert (terminal->name != NULL);
+  assert (terminal->fd < 0);
+  assert (terminal->fd_watch == NULL);
+
+  terminal->fd = open (terminal->name, O_RDWR | O_NOCTTY);
+
+  if (terminal->fd < 0)
+    return false;
+
+  terminal->fd_watch = ply_event_loop_watch_fd (terminal->loop, terminal->fd,
+                                                   PLY_EVENT_LOOP_FD_STATUS_NONE,
+                                                   (ply_event_handler_t) NULL,
+                                                   (ply_event_handler_t) on_tty_disconnected,
+                                                   terminal);
+
+  ply_terminal_check_for_vt (terminal);
+
+  return true;
+}
+
+bool
+ply_terminal_open (ply_terminal_t *terminal)
+{
+  assert (terminal != NULL);
+
+  if (terminal->name == NULL)
+    {
+      char tty_name[512] = "";
+
+      terminal->vt_number = get_active_vt ();
+
+      if (readlink ("/proc/self/fd/0", tty_name, sizeof (tty_name) - 1) < 0)
+        {
+          ply_trace ("could not read tty name of fd 0");
+          return false;
+        }
+
+      terminal->name = strdup (tty_name);
+    }
+
+  ply_trace ("trying to open terminal '%s'", terminal->name);
+
+  if (!ply_terminal_open_device (terminal))
+    {
+      ply_trace ("could not open %s : %m", terminal->name);
+      return false;
+    }
+
+  if (!ply_terminal_set_unbuffered_input (terminal))
+    ply_trace ("terminal '%s' will be line buffered", terminal->name);
+
+  ply_terminal_look_up_geometry (terminal);
+
+  ply_terminal_look_up_color_palette (terminal);
+  ply_terminal_save_color_palette (terminal);
+
+  ply_event_loop_watch_signal (terminal->loop,
+                               SIGWINCH,
+                               (ply_event_handler_t)
+                               ply_terminal_look_up_geometry,
+                               terminal);
+
+  terminal->is_open = true;
+
+  return true;
+}
+
+int
+ply_terminal_get_fd (ply_terminal_t *terminal)
+{
+  return terminal->fd;
+}
+
+bool
+ply_terminal_is_open (ply_terminal_t *terminal)
+{
+  return terminal->is_open;
+}
+
+void
+ply_terminal_close (ply_terminal_t *terminal)
+{
+  terminal->is_open = false;
+
+  ply_trace ("restoring color palette");
+  ply_terminal_restore_color_palette (terminal);
+
+  if (terminal->fd_watch != NULL)
+    {
+      ply_trace ("stop watching tty fd");
+      ply_event_loop_stop_watching_fd (terminal->loop, terminal->fd_watch);
+      terminal->fd_watch = NULL;
+    }
+
+  if (terminal->loop != NULL)
+    {
+      ply_trace ("stop watching SIGWINCH signal");
+      ply_event_loop_stop_watching_signal (terminal->loop, SIGWINCH);
+    }
+
+  ply_trace ("setting buffered input");
+  ply_terminal_set_buffered_input (terminal);
+
+  close (terminal->fd);
+  terminal->fd = -1;
+}
+
+int
+ply_terminal_get_number_of_columns (ply_terminal_t *terminal)
+{
+  return terminal->number_of_columns;
+}
+
+int
+ply_terminal_get_number_of_rows (ply_terminal_t *terminal)
+{
+  return terminal->number_of_rows;
+}
+
+uint32_t
+ply_terminal_get_color_hex_value (ply_terminal_t       *terminal,
+                                  ply_terminal_color_t  color)
+{
+  uint8_t red, green, blue;
+  uint32_t hex_value;
+
+  assert (terminal != NULL);
+  assert (color <= PLY_TERMINAL_COLOR_WHITE);
+
+  red = (uint8_t) *(terminal->color_palette + 3 * color);
+  green = (uint8_t) *(terminal->color_palette + 3 * color + 1);
+  blue = (uint8_t) *(terminal->color_palette + 3 * color + 2);
+
+  hex_value = red << 16 | green << 8 | blue;
+
+  return hex_value;
+}
+
+void
+ply_terminal_set_color_hex_value (ply_terminal_t       *terminal,
+                                  ply_terminal_color_t  color,
+                                  uint32_t              hex_value)
+{
+  uint8_t red, green, blue;
+
+  assert (terminal != NULL);
+  assert (color <= PLY_TERMINAL_COLOR_WHITE);
+
+  red = (uint8_t) ((hex_value >> 16) & 0xff);
+  green = (uint8_t) ((hex_value >> 8) & 0xff);
+  blue = (uint8_t) (hex_value & 0xff);
+
+  *(terminal->color_palette + 3 * color) = red;
+  *(terminal->color_palette + 3 * color + 1) = green;
+  *(terminal->color_palette + 3 * color + 2) = blue;
+
+  ply_terminal_change_color_palette (terminal);
+}
+
+bool
+ply_terminal_supports_color (ply_terminal_t *terminal)
+{
+  return terminal->supports_text_color;
+}
+
+static void
+ply_terminal_detach_from_event_loop (ply_terminal_t *terminal)
+{
+  assert (terminal != NULL);
+  terminal->loop = NULL;
+  terminal->fd_watch = NULL;
+}
+
+void
+ply_terminal_free (ply_terminal_t *terminal)
+{
+  if (terminal == NULL)
+    return;
+
+  free (terminal->name);
+
+  if (terminal->loop != NULL)
+    ply_event_loop_stop_watching_for_exit (terminal->loop,
+                                           (ply_event_loop_exit_handler_t)
+                                           ply_terminal_detach_from_event_loop,
+                                           terminal);
+
+  ply_terminal_close (terminal);
+
+  free (terminal);
+}
+
+int
+ply_terminal_get_vt_number (ply_terminal_t *terminal)
+{
+  return terminal->vt_number;
+}
+
+/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
diff --git a/src/libplybootsplash/ply-terminal.h b/src/libplybootsplash/ply-terminal.h
new file mode 100644 (file)
index 0000000..21689d1
--- /dev/null
@@ -0,0 +1,82 @@
+/* ply-terminal.h - APIs for terminaling text
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+#ifndef PLY_TERMINAL_H
+#define PLY_TERMINAL_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+
+typedef struct _ply_terminal ply_terminal_t;
+
+typedef enum
+{
+  PLY_TERMINAL_COLOR_BLACK = 0,
+  PLY_TERMINAL_COLOR_RED,
+  PLY_TERMINAL_COLOR_GREEN,
+  PLY_TERMINAL_COLOR_BROWN,
+  PLY_TERMINAL_COLOR_BLUE,
+  PLY_TERMINAL_COLOR_MAGENTA,
+  PLY_TERMINAL_COLOR_CYAN,
+  PLY_TERMINAL_COLOR_WHITE,
+  PLY_TERMINAL_COLOR_DEFAULT = PLY_TERMINAL_COLOR_WHITE + 2
+} ply_terminal_color_t;
+
+#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+ply_terminal_t *ply_terminal_new (const char *device_name);
+
+void ply_terminal_free (ply_terminal_t *terminal);
+
+bool ply_terminal_open (ply_terminal_t *terminal);
+int ply_terminal_get_fd (ply_terminal_t *terminal);
+bool ply_terminal_is_open (ply_terminal_t *terminal);
+void ply_terminal_close (ply_terminal_t *terminal);
+void ply_terminal_reset_colors (ply_terminal_t *terminal);
+
+bool ply_terminal_set_unbuffered_input (ply_terminal_t *terminal);
+bool ply_terminal_set_buffered_input (ply_terminal_t *terminal);
+
+__attribute__((__format__ (__printf__, 2, 3)))
+void ply_terminal_write (ply_terminal_t *terminal,
+                         const char     *format,
+                         ...);
+int ply_terminal_get_number_of_columns (ply_terminal_t *terminal);
+int ply_terminal_get_number_of_rows (ply_terminal_t *terminal);
+
+bool ply_terminal_supports_color (ply_terminal_t *terminal);
+uint32_t ply_terminal_get_color_hex_value (ply_terminal_t       *terminal,
+                                           ply_terminal_color_t  color);
+
+void ply_terminal_set_color_hex_value (ply_terminal_t       *terminal,
+                                       ply_terminal_color_t  color,
+                                       uint32_t              hex_value);
+
+int ply_terminal_get_vt_number (ply_terminal_t *terminal);
+
+#endif
+
+#endif /* PLY_TERMINAL_H */
+/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */