gtk-common.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * gtk-common.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 "unifont.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 ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
  41. #if GTK_CHECK_VERSION(2,0,0)
  42. ASSERT(sizeof(long) <= sizeof(gsize));
  43. #define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
  44. #define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
  45. #else /* Gtk 1.2 */
  46. ASSERT(sizeof(long) <= sizeof(gpointer));
  47. #define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
  48. #define GPOINTER_TO_LONG(p) ((long)(p))
  49. #endif
  50. /* ----------------------------------------------------------------------
  51. * File descriptors and uxsel.
  52. */
  53. struct uxsel_id {
  54. #if GTK_CHECK_VERSION(2,0,0)
  55. GIOChannel *chan;
  56. guint watch_id;
  57. #else
  58. int id;
  59. #endif
  60. };
  61. #if GTK_CHECK_VERSION(2,0,0)
  62. gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
  63. gpointer data)
  64. {
  65. int sourcefd = g_io_channel_unix_get_fd(source);
  66. /*
  67. * We must process exceptional notifications before ordinary
  68. * readability ones, or we may go straight past the urgent
  69. * marker.
  70. */
  71. if (condition & G_IO_PRI)
  72. select_result(sourcefd, SELECT_X);
  73. if (condition & (G_IO_IN | G_IO_HUP))
  74. select_result(sourcefd, SELECT_R);
  75. if (condition & G_IO_OUT)
  76. select_result(sourcefd, SELECT_W);
  77. run_toplevel_callbacks();
  78. return true;
  79. }
  80. #else
  81. void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
  82. {
  83. if (condition & GDK_INPUT_EXCEPTION)
  84. select_result(sourcefd, SELECT_X);
  85. if (condition & GDK_INPUT_READ)
  86. select_result(sourcefd, SELECT_R);
  87. if (condition & GDK_INPUT_WRITE)
  88. select_result(sourcefd, SELECT_W);
  89. run_toplevel_callbacks();
  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 & SELECT_R) flags |= G_IO_IN | G_IO_HUP;
  97. if (rwx & SELECT_W) flags |= G_IO_OUT;
  98. if (rwx & SELECT_X) 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 & SELECT_R) flags |= GDK_INPUT_READ;
  106. if (rwx & SELECT_W) flags |= GDK_INPUT_WRITE;
  107. if (rwx & SELECT_X) 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 bool idle_fn_scheduled;
  175. static void notify_toplevel_callback(void *);
  176. static gint idle_toplevel_callback_func(gpointer data)
  177. {
  178. run_toplevel_callbacks();
  179. /*
  180. * If we've emptied our toplevel callback queue, unschedule
  181. * ourself. Otherwise, leave ourselves pending so we'll be called
  182. * again to deal with more callbacks after another round of the
  183. * event loop.
  184. */
  185. if (!toplevel_callback_pending() && idle_fn_scheduled) {
  186. g_source_remove(toplevel_callback_idle_id);
  187. idle_fn_scheduled = false;
  188. }
  189. return true;
  190. }
  191. static void notify_toplevel_callback(void *vctx)
  192. {
  193. if (!idle_fn_scheduled) {
  194. toplevel_callback_idle_id =
  195. g_idle_add(idle_toplevel_callback_func, NULL);
  196. idle_fn_scheduled = true;
  197. }
  198. }
  199. /* ----------------------------------------------------------------------
  200. * Setup function. The real main program must call this.
  201. */
  202. void gtkcomm_setup(void)
  203. {
  204. uxsel_init();
  205. request_callback_notifications(notify_toplevel_callback, NULL);
  206. }