|  | /* | 
|  | * Server-side clipboard management | 
|  | * | 
|  | * Copyright (C) 2002 Ulrich Czekalla | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library 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 | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "ntstatus.h" | 
|  | #define WIN32_NO_STATUS | 
|  | #include "request.h" | 
|  | #include "object.h" | 
|  | #include "process.h" | 
|  | #include "user.h" | 
|  | #include "winuser.h" | 
|  | #include "winternl.h" | 
|  |  | 
|  | struct clipboard | 
|  | { | 
|  | struct object  obj;              /* object header */ | 
|  | struct thread *open_thread;      /* thread id that has clipboard open */ | 
|  | user_handle_t  open_win;         /* window that has clipboard open */ | 
|  | struct thread *owner_thread;     /* thread id that owns the clipboard */ | 
|  | user_handle_t  owner_win;        /* window that owns the clipboard data */ | 
|  | user_handle_t  viewer;           /* first window in clipboard viewer list */ | 
|  | unsigned int   seqno;            /* clipboard change sequence number */ | 
|  | time_t         seqno_timestamp;  /* time stamp of last seqno increment */ | 
|  | }; | 
|  |  | 
|  | static void clipboard_dump( struct object *obj, int verbose ); | 
|  |  | 
|  | static const struct object_ops clipboard_ops = | 
|  | { | 
|  | sizeof(struct clipboard),     /* size */ | 
|  | clipboard_dump,               /* dump */ | 
|  | no_get_type,                  /* get_type */ | 
|  | no_add_queue,                 /* add_queue */ | 
|  | NULL,                         /* remove_queue */ | 
|  | NULL,                         /* signaled */ | 
|  | NULL,                         /* satisfied */ | 
|  | no_signal,                    /* signal */ | 
|  | no_get_fd,                    /* get_fd */ | 
|  | no_map_access,                /* map_access */ | 
|  | default_get_sd,               /* get_sd */ | 
|  | default_set_sd,               /* set_sd */ | 
|  | no_lookup_name,               /* lookup_name */ | 
|  | no_open_file,                 /* open_file */ | 
|  | no_close_handle,              /* close_handle */ | 
|  | no_destroy                    /* destroy */ | 
|  | }; | 
|  |  | 
|  |  | 
|  | #define MINUPDATELAPSE 2 | 
|  |  | 
|  | /* dump a clipboard object */ | 
|  | static void clipboard_dump( struct object *obj, int verbose ) | 
|  | { | 
|  | struct clipboard *clipboard = (struct clipboard *)obj; | 
|  |  | 
|  | fprintf( stderr, "Clipboard open_thread=%p open_win=%08x owner_thread=%p owner_win=%08x viewer=%08x seq=%u\n", | 
|  | clipboard->open_thread, clipboard->open_win, clipboard->owner_thread, | 
|  | clipboard->owner_win, clipboard->viewer, clipboard->seqno ); | 
|  | } | 
|  |  | 
|  | /* retrieve the clipboard info for the current process, allocating it if needed */ | 
|  | static struct clipboard *get_process_clipboard(void) | 
|  | { | 
|  | struct clipboard *clipboard; | 
|  | struct winstation *winstation = get_process_winstation( current->process, WINSTA_ACCESSCLIPBOARD ); | 
|  |  | 
|  | if (!winstation) return NULL; | 
|  |  | 
|  | if (!(clipboard = winstation->clipboard)) | 
|  | { | 
|  | if ((clipboard = alloc_object( &clipboard_ops ))) | 
|  | { | 
|  | clipboard->open_thread = NULL; | 
|  | clipboard->open_win = 0; | 
|  | clipboard->owner_thread = NULL; | 
|  | clipboard->owner_win = 0; | 
|  | clipboard->viewer = 0; | 
|  | clipboard->seqno = 0; | 
|  | clipboard->seqno_timestamp = 0; | 
|  | winstation->clipboard = clipboard; | 
|  | } | 
|  | } | 
|  | release_object( winstation ); | 
|  | return clipboard; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Called when thread terminates to allow release of clipboard */ | 
|  | void cleanup_clipboard_thread(struct thread *thread) | 
|  | { | 
|  | struct clipboard *clipboard; | 
|  | struct winstation *winstation; | 
|  |  | 
|  | if (!thread->process->winstation) return; | 
|  | if (!(winstation = get_process_winstation( thread->process, WINSTA_ACCESSCLIPBOARD ))) return; | 
|  |  | 
|  | if ((clipboard = winstation->clipboard)) | 
|  | { | 
|  | if (thread == clipboard->open_thread) | 
|  | { | 
|  | clipboard->open_win = 0; | 
|  | clipboard->open_thread = NULL; | 
|  | } | 
|  | if (thread == clipboard->owner_thread) | 
|  | { | 
|  | clipboard->owner_win = 0; | 
|  | clipboard->owner_thread = NULL; | 
|  | } | 
|  | } | 
|  | release_object( winstation ); | 
|  | } | 
|  |  | 
|  | static int set_clipboard_window( struct clipboard *clipboard, user_handle_t win, int clear ) | 
|  | { | 
|  | if (clipboard->open_thread && clipboard->open_thread != current) | 
|  | { | 
|  | set_error(STATUS_WAS_LOCKED); | 
|  | return 0; | 
|  | } | 
|  | else if (!clear) | 
|  | { | 
|  | clipboard->open_win = win; | 
|  | clipboard->open_thread = current; | 
|  | } | 
|  | else | 
|  | { | 
|  | clipboard->open_thread = NULL; | 
|  | clipboard->open_win = 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int set_clipboard_owner( struct clipboard *clipboard, user_handle_t win, int clear ) | 
|  | { | 
|  | if (clipboard->open_thread && clipboard->open_thread->process != current->process) | 
|  | { | 
|  | set_error(STATUS_WAS_LOCKED); | 
|  | return 0; | 
|  | } | 
|  | else if (!clear) | 
|  | { | 
|  | clipboard->owner_win = win; | 
|  | clipboard->owner_thread = current; | 
|  | } | 
|  | else | 
|  | { | 
|  | clipboard->owner_win = 0; | 
|  | clipboard->owner_thread = NULL; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int get_seqno( struct clipboard *clipboard ) | 
|  | { | 
|  | time_t tm = time(NULL); | 
|  |  | 
|  | if (!clipboard->owner_thread && (tm > (clipboard->seqno_timestamp + MINUPDATELAPSE))) | 
|  | { | 
|  | clipboard->seqno_timestamp = tm; | 
|  | clipboard->seqno++; | 
|  | } | 
|  | return clipboard->seqno; | 
|  | } | 
|  |  | 
|  |  | 
|  | DECL_HANDLER(set_clipboard_info) | 
|  | { | 
|  | struct clipboard *clipboard = get_process_clipboard(); | 
|  |  | 
|  | if (!clipboard) return; | 
|  |  | 
|  | reply->old_clipboard = clipboard->open_win; | 
|  | reply->old_owner     = clipboard->owner_win; | 
|  | reply->old_viewer    = clipboard->viewer; | 
|  |  | 
|  | if (req->flags & SET_CB_OPEN) | 
|  | { | 
|  | if (clipboard->open_thread) | 
|  | { | 
|  | /* clipboard already opened */ | 
|  | set_error(STATUS_WAS_LOCKED); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!set_clipboard_window( clipboard, req->clipboard, 0 )) return; | 
|  | } | 
|  | else if (req->flags & SET_CB_CLOSE) | 
|  | { | 
|  | if (clipboard->open_thread != current) | 
|  | { | 
|  | set_win32_error(ERROR_CLIPBOARD_NOT_OPEN); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!set_clipboard_window( clipboard, 0, 1 )) return; | 
|  | } | 
|  |  | 
|  | if (req->flags & SET_CB_OWNER) | 
|  | { | 
|  | if (!set_clipboard_owner( clipboard, req->owner, 0 )) return; | 
|  | } | 
|  | else if (req->flags & SET_CB_RELOWNER) | 
|  | { | 
|  | if (!set_clipboard_owner( clipboard, 0, 1 )) return; | 
|  | } | 
|  |  | 
|  | if (req->flags & SET_CB_VIEWER) clipboard->viewer = req->viewer; | 
|  |  | 
|  | if (req->flags & SET_CB_SEQNO) clipboard->seqno++; | 
|  |  | 
|  | reply->seqno = get_seqno( clipboard ); | 
|  |  | 
|  | if (clipboard->open_thread == current) reply->flags |= CB_OPEN; | 
|  | if (clipboard->owner_thread == current) reply->flags |= CB_OWNER; | 
|  | if (clipboard->owner_thread && clipboard->owner_thread->process == current->process) | 
|  | reply->flags |= CB_PROCESS; | 
|  | } |