(Qforeground_color, Qbackground_color): Declare.
[bpt/emacs.git] / src / xselect.c.old
index a8c26f7..8a3e044 100644 (file)
@@ -1,11 +1,11 @@
 /* X Selection processing for emacs
-   Copyright (C) 1990 Free Software Foundation.
+   Copyright (C) 1990, 1992, 1993 Free Software Foundation.
 
 This file is part of GNU Emacs.
 
 GNU Emacs 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 1, or (at your option)
+the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -21,7 +21,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "lisp.h"
 #include "xterm.h"
 #include "buffer.h"
-#include "screen.h"
+#include "frame.h"
 
 #ifdef HAVE_X11
 
@@ -29,8 +29,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #define MAX_SELECTION(dpy) (((dpy)->max_request_size << 2) - 100)
 #define SELECTION_LENGTH(len,format) ((len) * ((format) >> 2))
 
-/* The last 23 bits of the timestamp of the last mouse button event. */
-extern Time mouse_timestamp;
+/* The timestamp of the last input event we received from the X server.  */
+unsigned long last_event_timestamp;
 
 /* t if a mouse button is depressed. */
 extern Lisp_Object Vmouse_grabbed;
@@ -53,7 +53,7 @@ Lisp_Object Vx_selection_value;
 /* The value of the current SECONDARY selection. */
 Lisp_Object Vx_secondary_selection_value;
 
-/* Types of selections we may make. */
+/* Types of selections we may make.  */
 Lisp_Object Qprimary, Qsecondary, Qclipboard;
 
 /* Emacs' selection property identifiers. */
@@ -108,7 +108,52 @@ int incr_nbytes;
 unsigned char *incr_value;
 unsigned char *incr_ptr;
 
-/* SELECTION OWNER CODE */
+/* Declarations for handling cut buffers.
+
+   Whenever we set a cut buffer or read a cut buffer's value, we cache
+   it in cut_buffer_value.  We look for PropertyNotify events about
+   the CUT_BUFFER properties, and invalidate our cache accordingly.
+   We ignore PropertyNotify events that we suspect were caused by our
+   own changes to the cut buffers, so we can keep the cache valid
+   longer.
+
+   IS ALL THIS HAIR WORTH IT?  Well, these functions get called every
+   time an element goes into or is retrieved from the kill ring, and
+   those ought to be quick.  It's not fun in time or space to wait for
+   50k cut buffers to fly back and forth across the net.  */
+
+/* The number of CUT_BUFFER properties defined under X.  */
+#define NUM_CUT_BUFFERS (8)
+
+/* cut_buffer_atom[n] is the atom naming the nth cut buffer.  */
+static Atom cut_buffer_atom[NUM_CUT_BUFFERS] = {
+  XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3,
+  XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7
+};
+
+/* cut_buffer_value is an eight-element vector;
+   (aref cut_buffer_value n) is the cached value of cut buffer n, or
+   Qnil if cut buffer n is unset.  */
+static Lisp_Object cut_buffer_value;
+
+/* Bit N of cut_buffer_cached is true if (aref cut_buffer_value n) is
+   known to be valid.  This is cleared by PropertyNotify events
+   handled by x_invalidate_cut_buffer_cache.  It would be wonderful if
+   that routine could just set the appropriate element of
+   cut_buffer_value to some special value meaning "uncached", but that
+   would lose if a GC happened to be in progress.
+
+   Bit N of cut_buffer_just_set is true if cut buffer N has been set since
+   the last PropertyNotify event; since we get an event even when we set
+   the property ourselves, we should ignore one event after setting
+   a cut buffer, so we don't have to throw away our cache.  */
+#ifdef __STDC__
+volatile
+#endif
+static cut_buffer_cached, cut_buffer_just_set;
+
+\f
+/* Acquiring ownership of a selection.  */
 
 
 /* Request selection ownership if we do not already have it. */
@@ -120,17 +165,20 @@ own_selection (selection_type, time)
 {
   Window owner_window, selecting_window;
 
-  if ((EQ (selection_type, Qprimary) && !NILP (Vx_selection_value))
-      || ((EQ (selection_type, Qsecondary)) && !NILP (Vx_secondary_selection_value))
-      || ((EQ (selection_type, Qclipboard)) && !NILP (Vx_clipboard_value)))
+  if ((selection_type == XA_PRIMARY
+       && !NILP (Vx_selection_value))
+      || (selection_type == XA_SECONDARY
+          && !NILP (Vx_secondary_selection_value))
+      || (selection_type == Xatom_clipboard
+          && !NILP (Vx_clipboard_value)))
     return 1;
 
-  selecting_window = selected_screen->display.x->window_desc;
+  selecting_window = FRAME_X_WINDOW (selected_frame);
   XSetSelectionOwner (x_current_display, selection_type,
                      selecting_window, time);
   owner_window = XGetSelectionOwner (x_current_display, selection_type);
 
-  if (owner_window != selecting_window)
+      if (owner_window != selecting_window)
     return 0;
 
   return 1;
@@ -140,19 +188,50 @@ own_selection (selection_type, time)
    If we are already the owner, merely change data and timestamp values.
    This avoids generating SelectionClear events for ourselves. */
 
-DEFUN ("x-own-selection", Fx_own_selection, Sx_own_selection,
-  1, 2, "",
-  "Make STRING the selection value.  Default is the primary selection,\n\
-but optional second argument TYPE may specify secondary or clipboard.")
-  (string, type)
-     register Lisp_Object string, type;
+DEFUN ("x-set-selection", Fx_set_selection, Sx_set_selection,
+  2, 2, "",
+  "Set the value of SELECTION to STRING.\n\
+SELECTION may be `primary', `secondary', or `clipboard'.\n\
+\n\
+Selections are a mechanism for cutting and pasting information between\n\
+X Windows clients.  Emacs's kill ring commands set the `primary'\n\
+selection to the top string of the kill ring, making it available to\n\
+other clients, like xterm.  Those commands also use the `primary'\n\
+selection to retrieve information from other clients.\n\
+\n\
+According to the Inter-Client Communications Conventions Manual:\n\
+\n\
+The `primary' selection \"... is used for all commands that take only a\n\
+   single argument and is the principal means of communication between\n\
+   clients that use the selection mechanism.\"  In Emacs, this means\n\
+   that the kill ring commands set the primary selection to the text\n\
+   put in the kill ring.\n\
+\n\
+The `secondary' selection \"... is used as the second argument to\n\
+   commands taking two arguments (for example, `exchange primary and\n\
+   secondary selections'), and as a means of obtaining data when there\n\
+   is a primary selection and the user does not want to disturb it.\"\n\
+   I am not sure how Emacs should use the secondary selection; if you\n\
+   come up with ideas, this function will at least let you get at it.\n\
+\n\
+The `clipboard' selection \"... is used to hold data that is being\n\
+   transferred between clients, that is, data that usually is being\n\
+   cut or copied, and then pasted.\"  It seems that the `clipboard'\n\
+   selection is for the most part equivalent to the `primary'\n\
+   selection, so Emacs sets them both.\n\
+\n\
+Also see `x-selection', and the `interprogram-cut-function' variable.")
+  (selection, string)
+     register Lisp_Object selection, string;
 {
   Atom selection_type;
   Lisp_Object val;
-  Time event_time = mouse_timestamp;
+  Time event_time = last_event_timestamp;
   CHECK_STRING (string, 0);
 
-  if (NILP (type) || EQ (type, Qprimary))
+  val = Qnil;
+
+  if (NILP (selection) || EQ (selection, Qprimary))
     {
       BLOCK_INPUT;
       if (own_selection (XA_PRIMARY, event_time))
@@ -162,7 +241,7 @@ but optional second argument TYPE may specify secondary or clipboard.")
        }
       UNBLOCK_INPUT;
     }
-  else if (EQ (type, Qsecondary))
+  else if (EQ (selection, Qsecondary))
     {
       BLOCK_INPUT;
       if (own_selection (XA_SECONDARY, event_time))
@@ -172,7 +251,7 @@ but optional second argument TYPE may specify secondary or clipboard.")
        }
       UNBLOCK_INPUT;
     }
-  else if (EQ (type, Qclipboard))
+  else if (EQ (selection, Qclipboard))
     {
       BLOCK_INPUT;
       if (own_selection (Xatom_clipboard, event_time))
@@ -197,7 +276,7 @@ x_disown_selection (old_owner, selection, changed_owner_time)
      Atom selection;
      Time changed_owner_time;
 {
-  struct screen *s = x_window_to_screen (old_owner);
+  struct frame *s = x_window_to_frame (old_owner);
 
   if (s)                       /* We are the owner */
     {
@@ -223,11 +302,14 @@ x_disown_selection (old_owner, selection, changed_owner_time)
     abort ();                  /* Inconsistent state. */
 }
 
+\f
+/* Answering selection requests.  */
+
 int x_selection_alloc_error;
 int x_converting_selection;
 
-/* Reply to some client's request for our selection data.  Data is
-   placed in a propery supplied by the requesting window.
+/* Reply to some client's request for our selection data.
+   Data is placed in a property supplied by the requesting window.
 
    If the data exceeds the maximum amount the server can send,
    then prepare to send it incrementally, and reply to the client with
@@ -485,7 +567,10 @@ x_send_incremental (event)
     }
 }
 
-/* SELECTION REQUESTOR CODE */
+\f
+/* Requesting the value of a selection.  */
+
+static Lisp_Object x_selection_arrival ();
 
 /* Predicate function used to match a requested event. */
 
@@ -515,8 +600,8 @@ get_selection_value (type)
   Window requestor_window;
 
   BLOCK_INPUT;
-  requestor_time = mouse_timestamp;
-  requestor_window = selected_screen->display.x->window_desc;
+  requestor_time = last_event_timestamp;
+  requestor_window = FRAME_X_WINDOW (selected_frame);
   XConvertSelection (x_current_display, type, XA_STRING,
                     Xatom_emacs_selection, requestor_window, requestor_time);
   XIfEvent (x_current_display,
@@ -533,30 +618,39 @@ get_selection_value (type)
    simply return our selection value.  If we are not the owner, this
    will block until all of the data has arrived. */
 
-DEFUN ("x-selection-value", Fx_selection_value, Sx_selection_value,
-  0, 1, "",
-  "Return the value of one of the selections.  Default is the primary\n\
-selection, but optional argument TYPE may specify secondary or clipboard.")
-  (type)
-     register Lisp_Object type;
+DEFUN ("x-selection", Fx_selection, Sx_selection,
+  1, 1, "",
+  "Return the value of SELECTION.\n\
+SELECTION is one of `primary', `secondary', or `clipboard'.\n\
+\n\
+Selections are a mechanism for cutting and pasting information between\n\
+X Windows clients.  When the user selects text in an X application,\n\
+the application should set the primary selection to that text; Emacs's\n\
+kill ring commands will then check the value of the `primary'\n\
+selection, and return it as the most recent kill.\n\
+The documentation for `x-set-selection' gives more information on how\n\
+the different selection types are intended to be used.\n\
+Also see the `interprogram-paste-function' variable.")
+  (selection)
+     register Lisp_Object selection;
 {
   Atom selection_type;
 
-  if (NILP (type) || EQ (type, Qprimary))
+  if (NILP (selection) || EQ (selection, Qprimary))
     {
       if (!NILP (Vx_selection_value))
        return Vx_selection_value;
 
       return get_selection_value (XA_PRIMARY);
     }
-  else if (EQ (type, Qsecondary))
+  else if (EQ (selection, Qsecondary))
     {
       if (!NILP (Vx_secondary_selection_value))
        return Vx_secondary_selection_value;
 
       return get_selection_value (XA_SECONDARY);
     }
-  else if (EQ (type, Qclipboard))
+  else if (EQ (selection, Qclipboard))
     {
       if (!NILP (Vx_clipboard_value))
        return Vx_clipboard_value;
@@ -567,7 +661,7 @@ selection, but optional argument TYPE may specify secondary or clipboard.")
     error ("Invalid X selection type");
 }
 
-Lisp_Object
+static Lisp_Object
 x_selection_arrival (event, requestor_window, requestor_time)
      register XSelectionEvent *event;
      Window requestor_window;
@@ -680,6 +774,148 @@ x_selection_arrival (event, requestor_window, requestor_time)
   return Qnil;
 }
 
+\f
+/* Cut buffer management.  */
+
+DEFUN ("x-get-cut-buffer", Fx_get_cut_buffer, Sx_get_cut_buffer, 0, 1, "",
+  "Return the value of cut buffer N, or nil if it is unset.\n\
+If N is omitted, it defaults to zero.\n\
+Note that cut buffers have some problems that selections don't; try to\n\
+write your code to use cut buffers only for backward compatibility,\n\
+and use selections for the serious work.")
+  (n)
+     Lisp_Object n;
+{
+  int buf_num;
+
+  if (NILP (n))
+    buf_num = 0;
+  else
+    {
+      CHECK_NUMBER (n, 0);
+      buf_num = XINT (n);
+    }
+
+  if (buf_num < 0 || buf_num >= NUM_CUT_BUFFERS)
+    error ("cut buffer numbers must be from zero to seven");
+
+  {
+    Lisp_Object value;
+
+    /* Note that no PropertyNotify events will be processed while
+       input is blocked.  */
+    BLOCK_INPUT;
+
+    if (cut_buffer_cached & (1 << buf_num))
+      value = XVECTOR (cut_buffer_value)->contents[buf_num];
+    else
+      {
+       /* Our cache is invalid; retrieve the property's value from
+          the server.  */
+       int buf_len;
+       char *buf = XFetchBuffer (x_current_display, &buf_len, buf_num);
+
+       if (buf_len == 0)
+         value = Qnil;
+       else
+         value = make_string (buf, buf_len);
+
+       XVECTOR (cut_buffer_value)->contents[buf_num] = value;
+       cut_buffer_cached |= (1 << buf_num);
+
+       XFree (buf);
+      }
+
+    UNBLOCK_INPUT;
+
+    return value;
+  }
+}
+
+DEFUN ("x-set-cut-buffer", Fx_set_cut_buffer, Sx_set_cut_buffer, 2, 2, "",
+  "Set the value of cut buffer N to STRING.\n\
+Note that cut buffers have some problems that selections don't; try to\n\
+write your code to use cut buffers only for backward compatibility,\n\
+and use selections for the serious work.")
+  (n, string)
+     Lisp_Object n, string;
+{
+  int buf_num;
+
+  CHECK_NUMBER (n, 0);
+  CHECK_STRING (string, 1);
+
+  buf_num = XINT (n);
+
+  if (buf_num < 0 || buf_num >= NUM_CUT_BUFFERS)
+    error ("cut buffer numbers must be from zero to seven");
+
+  BLOCK_INPUT;
+
+  /* DECwindows and some other servers don't seem to like setting
+     properties to values larger than about 20k.  For very large
+     values, they signal an error, but for intermediate values they
+     just seem to hang.
+
+     We could just truncate the request, but it's better to let the
+     user know that the strategy he/she's using isn't going to work
+     than to have it work partially, but incorrectly.  */
+
+  if (XSTRING (string)->size == 0
+      || XSTRING (string)->size > MAX_SELECTION (x_current_display))
+    {
+      XStoreBuffer (x_current_display, (char *) 0, 0, buf_num);
+      string = Qnil;
+    }
+  else
+    {
+      XStoreBuffer (x_current_display,
+                   (char *) XSTRING (string)->data, XSTRING (string)->size,
+                   buf_num);
+    }
+
+  XVECTOR (cut_buffer_value)->contents[buf_num] = string;
+  cut_buffer_cached |= (1 << buf_num);
+  cut_buffer_just_set |= (1 << buf_num);
+
+  UNBLOCK_INPUT;
+
+  return string;
+}
+
+/* Ask the server to send us an event if any cut buffer is modified.  */
+
+void
+x_watch_cut_buffer_cache ()
+{
+  XSelectInput (x_current_display, ROOT_WINDOW, PropertyChangeMask);
+}
+
+/* The server has told us that a cut buffer has been modified; deal with that.
+   Note that this function is called at interrupt level.  */
+void
+x_invalidate_cut_buffer_cache (XPropertyEvent *event)
+{
+  int i;
+
+  /* See which cut buffer this is about, if any.  */
+  for (i = 0; i < NUM_CUT_BUFFERS; i++)
+    if (event->atom == cut_buffer_atom[i])
+      {
+       int mask = (1 << i);
+
+       if (cut_buffer_just_set & mask)
+         cut_buffer_just_set &= ~mask;
+       else
+         cut_buffer_cached &= ~mask;
+
+       break;
+      }
+}
+
+\f
+/* Bureaucracy.  */
+
 void
 syms_of_xselect ()
 {
@@ -702,7 +938,13 @@ syms_of_xselect ()
   Qclipboard = intern ("clipboard");
   staticpro (&Qclipboard);
 
-  defsubr (&Sx_own_selection);
-  defsubr (&Sx_selection_value);
+  defsubr (&Sx_set_selection);
+  defsubr (&Sx_selection);
+
+  cut_buffer_value = Fmake_vector (make_number (NUM_CUT_BUFFERS), Qnil);
+  staticpro (&cut_buffer_value);
+
+  defsubr (&Sx_get_cut_buffer);
+  defsubr (&Sx_set_cut_buffer);
 }
 #endif /* X11 */