| /* | |
| * 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 <assert.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include "wine/list.h" | |
| #include "request.h" | |
| #include "object.h" | |
| #include "process.h" | |
| #include "user.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; | |
| 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 (!grab_global_atom( req->atom )) return; | |
| if (!(class = create_class( current->process, req->extra, req->local ))) | |
| { | |
| release_global_atom( req->atom ); | |
| 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; | |
| } | |
| /* 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) | |
| { | |
| if (!grab_global_atom( req->atom )) return; | |
| release_global_atom( class->atom ); | |
| class->atom = req->atom; | |
| } | |
| 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 ); | |
| } |