/* * Server-side window class management * * Copyright (C) 2002 Mike McCormack * Copyright (C) 2003 Alexandre Julliard * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "wine/port.h" #include #include #include #include #include "wine/list.h" #include "request.h" #include "object.h" #include "process.h" #include "user.h" #include "winuser.h" struct window_class { struct list entry; /* entry in process list */ struct process *process; /* process owning the class */ int count; /* reference count */ int local; /* local class? */ atom_t atom; /* class atom */ void *instance; /* module instance */ unsigned int style; /* class style */ int win_extra; /* number of window extra bytes */ void *client_ptr; /* pointer to class in client address space */ int nb_extra_bytes; /* number of extra bytes */ char extra_bytes[1]; /* extra bytes storage */ }; #define DESKTOP_ATOM ((atom_t)32769) static struct window_class *desktop_class; static struct window_class *create_class( struct process *process, int extra_bytes, int local ) { struct window_class *class; if (!(class = mem_alloc( sizeof(*class) + extra_bytes - 1 ))) return NULL; class->process = (struct process *)grab_object( process ); class->count = 0; class->local = local; class->nb_extra_bytes = extra_bytes; memset( class->extra_bytes, 0, extra_bytes ); /* other fields are initialized by caller */ /* local classes have priority so we put them first in the list */ if (local) list_add_head( &process->classes, &class->entry ); else list_add_tail( &process->classes, &class->entry ); return class; } static struct window_class *create_desktop_class( unsigned int style, int win_extra ) { struct window_class *class; if (!(class = mem_alloc( sizeof(*class) - 1 ))) return NULL; class->process = NULL; class->count = 0; class->local = 0; class->nb_extra_bytes = 0; class->atom = DESKTOP_ATOM; class->instance = NULL; class->style = style; class->win_extra = win_extra; class->client_ptr = NULL; desktop_class = class; return class; } static void destroy_class( struct window_class *class ) { list_remove( &class->entry ); release_object( class->process ); free( class ); } void destroy_process_classes( struct process *process ) { struct list *ptr; while ((ptr = list_head( &process->classes ))) { struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry ); destroy_class( class ); } } static struct window_class *find_class( struct process *process, atom_t atom, void *instance ) { struct list *ptr; LIST_FOR_EACH( ptr, &process->classes ) { struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry ); if (class->atom != atom) continue; if (!instance || !class->local || class->instance == instance) return class; } if (atom == DESKTOP_ATOM) return desktop_class; return NULL; } struct window_class *grab_class( struct process *process, atom_t atom, void *instance, int *extra_bytes ) { struct window_class *class = find_class( process, atom, instance ); if (class) { class->count++; *extra_bytes = class->win_extra; } else set_error( STATUS_INVALID_HANDLE ); return class; } void release_class( struct window_class *class ) { assert( class->count > 0 ); class->count--; } atom_t get_class_atom( struct window_class *class ) { return class->atom; } void *get_class_client_ptr( struct window_class *class ) { return class->client_ptr; } /* create a window class */ DECL_HANDLER(create_class) { struct window_class *class; struct winstation *winstation; if (!req->local && req->atom == DESKTOP_ATOM) { if (!desktop_class) create_desktop_class( req->style, req->win_extra ); return; /* silently ignore further attempts to create the desktop class */ } class = find_class( current->process, req->atom, req->instance ); if (class && !class->local == !req->local) { set_win32_error( ERROR_CLASS_ALREADY_EXISTS ); return; } if (req->extra < 0 || req->extra > 4096 || req->win_extra < 0 || req->win_extra > 4096) { /* don't allow stupid values here */ set_error( STATUS_INVALID_PARAMETER ); return; } if (!(winstation = get_process_winstation( current->process, WINSTA_ACCESSGLOBALATOMS ))) return; if (!grab_global_atom( winstation, req->atom )) { release_object( winstation ); return; } if (!(class = create_class( current->process, req->extra, req->local ))) { release_global_atom( winstation, req->atom ); release_object( winstation ); return; } class->atom = req->atom; class->instance = req->instance; class->style = req->style; class->win_extra = req->win_extra; class->client_ptr = req->client_ptr; release_object( winstation ); } /* destroy a window class */ DECL_HANDLER(destroy_class) { struct window_class *class = find_class( current->process, req->atom, req->instance ); if (!class) set_win32_error( ERROR_CLASS_DOES_NOT_EXIST ); else if (class->count) set_win32_error( ERROR_CLASS_HAS_WINDOWS ); else { reply->client_ptr = class->client_ptr; if (class != desktop_class) destroy_class( class ); } } /* set some information in a class */ DECL_HANDLER(set_class_info) { struct window_class *class = get_window_class( req->window ); if (!class) return; if (req->flags && class->process != current->process) { set_error( STATUS_ACCESS_DENIED ); return; } if (req->extra_size > sizeof(req->extra_value) || req->extra_offset < -1 || req->extra_offset > class->nb_extra_bytes - (int)req->extra_size) { set_win32_error( ERROR_INVALID_INDEX ); return; } if ((req->flags & SET_CLASS_WINEXTRA) && (req->win_extra < 0 || req->win_extra > 4096)) { set_error( STATUS_INVALID_PARAMETER ); return; } if (req->extra_offset != -1) { memcpy( &reply->old_extra_value, class->extra_bytes + req->extra_offset, req->extra_size ); } else if (req->flags & SET_CLASS_EXTRA) { set_win32_error( ERROR_INVALID_INDEX ); return; } reply->old_atom = class->atom; reply->old_style = class->style; reply->old_extra = class->nb_extra_bytes; reply->old_win_extra = class->win_extra; reply->old_instance = class->instance; if (req->flags & SET_CLASS_ATOM) { struct winstation *winstation = get_process_winstation( current->process, WINSTA_ACCESSGLOBALATOMS ); if (!grab_global_atom( winstation, req->atom )) { release_object( winstation ); return; } release_global_atom( winstation, class->atom ); class->atom = req->atom; release_object( winstation ); } if (req->flags & SET_CLASS_STYLE) class->style = req->style; if (req->flags & SET_CLASS_WINEXTRA) class->win_extra = req->win_extra; if (req->flags & SET_CLASS_INSTANCE) class->instance = req->instance; if (req->flags & SET_CLASS_EXTRA) memcpy( class->extra_bytes + req->extra_offset, &req->extra_value, req->extra_size ); }