gtkcomm.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * gtkcomm.c: machinery in the GTK front end which is common to all
  3. * programs that run a session in a terminal window, and also common
  4. * across all _sessions_ rather than specific to one session. (Timers,
  5. * uxsel etc.)
  6. */
  7. #define _GNU_SOURCE
  8. #include <string.h>
  9. #include <assert.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <signal.h>
  13. #include <stdio.h>
  14. #include <time.h>
  15. #include <errno.h>
  16. #include <locale.h>
  17. #include <fcntl.h>
  18. #include <unistd.h>
  19. #include <sys/types.h>
  20. #include <sys/wait.h>
  21. #include <gtk/gtk.h>
  22. #if !GTK_CHECK_VERSION(3,0,0)
  23. #include <gdk/gdkkeysyms.h>
  24. #endif
  25. #if GTK_CHECK_VERSION(2,0,0)
  26. #include <gtk/gtkimmodule.h>
  27. #endif
  28. #define MAY_REFER_TO_GTK_IN_HEADERS
  29. #include "putty.h"
  30. #include "terminal.h"
  31. #include "gtkcompat.h"
  32. #include "gtkfont.h"
  33. #include "gtkmisc.h"
  34. #ifndef NOT_X_WINDOWS
  35. #include <gdk/gdkx.h>
  36. #include <X11/Xlib.h>
  37. #include <X11/Xutil.h>
  38. #include <X11/Xatom.h>
  39. #endif
  40. #define CAT2(x,y) x ## y
  41. #define CAT(x,y) CAT2(x,y)
  42. #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
  43. #if GTK_CHECK_VERSION(2,0,0)
  44. ASSERT(sizeof(long) <= sizeof(gsize));
  45. #define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
  46. #define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
  47. #else /* Gtk 1.2 */
  48. ASSERT(sizeof(long) <= sizeof(gpointer));
  49. #define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
  50. #define GPOINTER_TO_LONG(p) ((long)(p))
  51. #endif
  52. /* ----------------------------------------------------------------------
  53. * File descriptors and uxsel.
  54. */
  55. struct uxsel_id {
  56. #if GTK_CHECK_VERSION(2,0,0)
  57. GIOChannel *chan;
  58. guint watch_id;
  59. #else
  60. int id;
  61. #endif
  62. };
  63. #if GTK_CHECK_VERSION(2,0,0)
  64. gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
  65. gpointer data)
  66. {
  67. int sourcefd = g_io_channel_unix_get_fd(source);
  68. /*
  69. * We must process exceptional notifications before ordinary
  70. * readability ones, or we may go straight past the urgent
  71. * marker.
  72. */
  73. if (condition & G_IO_PRI)
  74. select_result(sourcefd, 4);
  75. if (condition & G_IO_IN)
  76. select_result(sourcefd, 1);
  77. if (condition & G_IO_OUT)
  78. select_result(sourcefd, 2);
  79. return TRUE;
  80. }
  81. #else
  82. void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
  83. {
  84. if (condition & GDK_INPUT_EXCEPTION)
  85. select_result(sourcefd, 4);
  86. if (condition & GDK_INPUT_READ)
  87. select_result(sourcefd, 1);
  88. if (condition & GDK_INPUT_WRITE)
  89. select_result(sourcefd, 2);
  90. }
  91. #endif
  92. uxsel_id *uxsel_input_add(int fd, int rwx) {
  93. uxsel_id *id = snew(uxsel_id);
  94. #if GTK_CHECK_VERSION(2,0,0)
  95. int flags = 0;
  96. if (rwx & 1) flags |= G_IO_IN;
  97. if (rwx & 2) flags |= G_IO_OUT;
  98. if (rwx & 4) flags |= G_IO_PRI;
  99. id->chan = g_io_channel_unix_new(fd);
  100. g_io_channel_set_encoding(id->chan, NULL, NULL);
  101. id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags,
  102. fd_input_func, NULL, NULL);
  103. #else
  104. int flags = 0;
  105. if (rwx & 1) flags |= GDK_INPUT_READ;
  106. if (rwx & 2) flags |= GDK_INPUT_WRITE;
  107. if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
  108. assert(flags);
  109. id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
  110. #endif
  111. return id;
  112. }
  113. void uxsel_input_remove(uxsel_id *id) {
  114. #if GTK_CHECK_VERSION(2,0,0)
  115. g_source_remove(id->watch_id);
  116. g_io_channel_unref(id->chan);
  117. #else
  118. gdk_input_remove(id->id);
  119. #endif
  120. sfree(id);
  121. }
  122. /* ----------------------------------------------------------------------
  123. * Timers.
  124. */
  125. static guint timer_id = 0;
  126. static gint timer_trigger(gpointer data)
  127. {
  128. unsigned long now = GPOINTER_TO_LONG(data);
  129. unsigned long next, then;
  130. long ticks;
  131. /*
  132. * Destroy the timer we got here on.
  133. */
  134. if (timer_id) {
  135. g_source_remove(timer_id);
  136. timer_id = 0;
  137. }
  138. /*
  139. * run_timers() may cause a call to timer_change_notify, in which
  140. * case a new timer will already have been set up and left in
  141. * timer_id. If it hasn't, and run_timers reports that some timing
  142. * still needs to be done, we do it ourselves.
  143. */
  144. if (run_timers(now, &next) && !timer_id) {
  145. then = now;
  146. now = GETTICKCOUNT();
  147. if (now - then > next - then)
  148. ticks = 0;
  149. else
  150. ticks = next - now;
  151. timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
  152. }
  153. /*
  154. * Returning FALSE means 'don't call this timer again', which
  155. * _should_ be redundant given that we removed it above, but just
  156. * in case, return FALSE anyway.
  157. */
  158. return FALSE;
  159. }
  160. void timer_change_notify(unsigned long next)
  161. {
  162. long ticks;
  163. if (timer_id)
  164. g_source_remove(timer_id);
  165. ticks = next - GETTICKCOUNT();
  166. if (ticks <= 0)
  167. ticks = 1; /* just in case */
  168. timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
  169. }
  170. /* ----------------------------------------------------------------------
  171. * Toplevel callbacks.
  172. */
  173. static guint toplevel_callback_idle_id;
  174. static int idle_fn_scheduled;
  175. static void notify_toplevel_callback(void *);
  176. /*
  177. * Replacement code for the gtk_quit_add() function, which GTK2 - in
  178. * their unbounded wisdom - deprecated without providing any usable
  179. * replacement, and which we were using to ensure that our idle
  180. * function for toplevel callbacks was only run from the outermost
  181. * gtk_main().
  182. *
  183. * We must make sure that all our subsidiary calls to gtk_main() are
  184. * followed by a call to post_main(), so that the idle function can be
  185. * re-established when we end up back at the top level.
  186. */
  187. void post_main(void)
  188. {
  189. if (gtk_main_level() == 1)
  190. notify_toplevel_callback(NULL);
  191. }
  192. static gint idle_toplevel_callback_func(gpointer data)
  193. {
  194. if (gtk_main_level() > 1) {
  195. /*
  196. * We don't run callbacks if we're in the middle of a
  197. * subsidiary gtk_main. So unschedule this idle function; it
  198. * will be rescheduled by post_main() when we come back up a
  199. * level, which is the earliest we might actually do
  200. * something.
  201. */
  202. if (idle_fn_scheduled) { /* double-check, just in case */
  203. g_source_remove(toplevel_callback_idle_id);
  204. idle_fn_scheduled = FALSE;
  205. }
  206. } else {
  207. run_toplevel_callbacks();
  208. }
  209. /*
  210. * If we've emptied our toplevel callback queue, unschedule
  211. * ourself. Otherwise, leave ourselves pending so we'll be called
  212. * again to deal with more callbacks after another round of the
  213. * event loop.
  214. */
  215. if (!toplevel_callback_pending() && idle_fn_scheduled) {
  216. g_source_remove(toplevel_callback_idle_id);
  217. idle_fn_scheduled = FALSE;
  218. }
  219. return TRUE;
  220. }
  221. static void notify_toplevel_callback(void *frontend)
  222. {
  223. if (!idle_fn_scheduled) {
  224. toplevel_callback_idle_id =
  225. g_idle_add(idle_toplevel_callback_func, NULL);
  226. idle_fn_scheduled = TRUE;
  227. }
  228. }
  229. /* ----------------------------------------------------------------------
  230. * Setup function. The real main program must call this.
  231. */
  232. void gtkcomm_setup(void)
  233. {
  234. uxsel_init();
  235. request_callback_notifications(notify_toplevel_callback, NULL);
  236. }