Add more tests for text placement in single and multiline edit
[wine] / server / class.c
1 /*
2  * Server-side window class management
3  *
4  * Copyright (C) 2002 Mike McCormack
5  * Copyright (C) 2003 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "wine/list.h"
31
32 #include "request.h"
33 #include "object.h"
34 #include "process.h"
35 #include "user.h"
36
37 struct window_class
38 {
39     struct list     entry;           /* entry in process list */
40     struct process *process;         /* process owning the class */
41     int             count;           /* reference count */
42     int             local;           /* local class? */
43     atom_t          atom;            /* class atom */
44     void           *instance;        /* module instance */
45     unsigned int    style;           /* class style */
46     int             win_extra;       /* number of window extra bytes */
47     void           *client_ptr;      /* pointer to class in client address space */
48     int             nb_extra_bytes;  /* number of extra bytes */
49     char            extra_bytes[1];  /* extra bytes storage */
50 };
51
52 #define DESKTOP_ATOM  ((atom_t)32769)
53
54 static struct window_class *desktop_class;
55
56 static struct window_class *create_class( struct process *process, int extra_bytes, int local )
57 {
58     struct window_class *class;
59
60     if (!(class = mem_alloc( sizeof(*class) + extra_bytes - 1 ))) return NULL;
61
62     class->process = (struct process *)grab_object( process );
63     class->count = 0;
64     class->local = local;
65     class->nb_extra_bytes = extra_bytes;
66     memset( class->extra_bytes, 0, extra_bytes );
67     /* other fields are initialized by caller */
68
69     /* local classes have priority so we put them first in the list */
70     if (local) list_add_head( &process->classes, &class->entry );
71     else list_add_tail( &process->classes, &class->entry );
72     return class;
73 }
74
75 static struct window_class *create_desktop_class( unsigned int style, int win_extra )
76 {
77     struct window_class *class;
78
79     if (!(class = mem_alloc( sizeof(*class) - 1 ))) return NULL;
80
81     class->process        = NULL;
82     class->count          = 0;
83     class->local          = 0;
84     class->nb_extra_bytes = 0;
85     class->atom           = DESKTOP_ATOM;
86     class->instance       = NULL;
87     class->style          = style;
88     class->win_extra      = win_extra;
89     class->client_ptr     = NULL;
90     desktop_class = class;
91     return class;
92 }
93
94 static void destroy_class( struct window_class *class )
95 {
96     list_remove( &class->entry );
97     release_object( class->process );
98     free( class );
99 }
100
101 void destroy_process_classes( struct process *process )
102 {
103     struct list *ptr;
104
105     while ((ptr = list_head( &process->classes )))
106     {
107         struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry );
108         destroy_class( class );
109     }
110 }
111
112 static struct window_class *find_class( struct process *process, atom_t atom, void *instance )
113 {
114     struct list *ptr;
115
116     LIST_FOR_EACH( ptr, &process->classes )
117     {
118         struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry );
119         if (class->atom != atom) continue;
120         if (!instance || !class->local || class->instance == instance) return class;
121     }
122     if (atom == DESKTOP_ATOM) return desktop_class;
123     return NULL;
124 }
125
126 struct window_class *grab_class( struct process *process, atom_t atom,
127                                  void *instance, int *extra_bytes )
128 {
129     struct window_class *class = find_class( process, atom, instance );
130     if (class)
131     {
132         class->count++;
133         *extra_bytes = class->win_extra;
134     }
135     else set_error( STATUS_INVALID_HANDLE );
136     return class;
137 }
138
139 void release_class( struct window_class *class )
140 {
141     assert( class->count > 0 );
142     class->count--;
143 }
144
145 atom_t get_class_atom( struct window_class *class )
146 {
147     return class->atom;
148 }
149
150 void *get_class_client_ptr( struct window_class *class )
151 {
152     return class->client_ptr;
153 }
154
155 /* create a window class */
156 DECL_HANDLER(create_class)
157 {
158     struct window_class *class;
159
160     if (!req->local && req->atom == DESKTOP_ATOM)
161     {
162         if (!desktop_class) create_desktop_class( req->style, req->win_extra );
163         return;  /* silently ignore further attempts to create the desktop class */
164     }
165
166     class = find_class( current->process, req->atom, req->instance );
167     if (class && !class->local == !req->local)
168     {
169         set_win32_error( ERROR_CLASS_ALREADY_EXISTS );
170         return;
171     }
172     if (req->extra < 0 || req->extra > 4096 || req->win_extra < 0 || req->win_extra > 4096)
173     {
174         /* don't allow stupid values here */
175         set_error( STATUS_INVALID_PARAMETER );
176         return;
177     }
178     if (!grab_global_atom( req->atom )) return;
179
180     if (!(class = create_class( current->process, req->extra, req->local )))
181     {
182         release_global_atom( req->atom );
183         return;
184     }
185     class->atom       = req->atom;
186     class->instance   = req->instance;
187     class->style      = req->style;
188     class->win_extra  = req->win_extra;
189     class->client_ptr = req->client_ptr;
190 }
191
192 /* destroy a window class */
193 DECL_HANDLER(destroy_class)
194 {
195     struct window_class *class = find_class( current->process, req->atom, req->instance );
196
197     if (!class)
198         set_win32_error( ERROR_CLASS_DOES_NOT_EXIST );
199     else if (class->count)
200         set_win32_error( ERROR_CLASS_HAS_WINDOWS );
201     else
202     {
203         reply->client_ptr = class->client_ptr;
204         if (class != desktop_class) destroy_class( class );
205     }
206 }
207
208
209 /* set some information in a class */
210 DECL_HANDLER(set_class_info)
211 {
212     struct window_class *class = get_window_class( req->window );
213
214     if (!class) return;
215
216     if (req->flags && class->process != current->process)
217     {
218         set_error( STATUS_ACCESS_DENIED );
219         return;
220     }
221
222     if (req->extra_size > sizeof(req->extra_value) ||
223         req->extra_offset < -1 ||
224         req->extra_offset > class->nb_extra_bytes - (int)req->extra_size)
225     {
226         set_win32_error( ERROR_INVALID_INDEX );
227         return;
228     }
229     if ((req->flags & SET_CLASS_WINEXTRA) && (req->win_extra < 0 || req->win_extra > 4096))
230     {
231         set_error( STATUS_INVALID_PARAMETER );
232         return;
233     }
234     if (req->extra_offset != -1)
235     {
236         memcpy( &reply->old_extra_value, class->extra_bytes + req->extra_offset, req->extra_size );
237     }
238     else if (req->flags & SET_CLASS_EXTRA)
239     {
240         set_win32_error( ERROR_INVALID_INDEX );
241         return;
242     }
243
244     reply->old_atom      = class->atom;
245     reply->old_style     = class->style;
246     reply->old_extra     = class->nb_extra_bytes;
247     reply->old_win_extra = class->win_extra;
248     reply->old_instance  = class->instance;
249
250     if (req->flags & SET_CLASS_ATOM)
251     {
252         if (!grab_global_atom( req->atom )) return;
253         release_global_atom( class->atom );
254         class->atom = req->atom;
255     }
256     if (req->flags & SET_CLASS_STYLE) class->style = req->style;
257     if (req->flags & SET_CLASS_WINEXTRA) class->win_extra = req->win_extra;
258     if (req->flags & SET_CLASS_INSTANCE) class->instance = req->instance;
259     if (req->flags & SET_CLASS_EXTRA) memcpy( class->extra_bytes + req->extra_offset,
260                                               &req->extra_value, req->extra_size );
261 }