wine/server/class.c
Jinoh Kang 17e6ef6d7e server: Correctly expose composited parent window and its child on position change.
Skip redrawing the composited child window when the window rect and
visible region stays the same, since we're taking the union of the old
and new visible regions.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53153
Signed-off-by: Jinoh Kang <jinoh.kang.kr@gmail.com>
2022-11-04 19:23:56 +01:00

301 lines
9 KiB
C

/*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "wine/list.h"
#include "request.h"
#include "object.h"
#include "process.h"
#include "user.h"
#include "winuser.h"
#include "winternl.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 */
atom_t base_atom; /* base class atom for versioned class */
mod_handle_t instance; /* module instance */
unsigned int style; /* class style */
int win_extra; /* number of window extra bytes */
client_ptr_t client_ptr; /* pointer to class in client address space */
int nb_extra_bytes; /* number of extra bytes */
char extra_bytes[1]; /* extra bytes storage */
};
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 void destroy_class( struct window_class *class )
{
release_global_atom( NULL, class->atom );
release_global_atom( NULL, class->base_atom );
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, mod_handle_t instance )
{
struct list *ptr;
LIST_FOR_EACH( ptr, &process->classes )
{
int is_win16;
struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry );
if (class->atom != atom) continue;
is_win16 = !(class->instance >> 16);
if (!instance || !class->local || class->instance == instance ||
(!is_win16 && ((class->instance & ~0xffff) == (instance & ~0xffff)))) return class;
}
return NULL;
}
struct window_class *grab_class( struct process *process, atom_t atom,
mod_handle_t 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--;
}
int is_desktop_class( struct window_class *class )
{
return (class->atom == DESKTOP_ATOM && !class->local);
}
int is_hwnd_message_class( struct window_class *class )
{
static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
static const struct unicode_str name = { messageW, sizeof(messageW) };
return (!class->local && class->atom == find_global_atom( NULL, &name ));
}
int get_class_style( struct window_class *class )
{
return class->style;
}
atom_t get_class_atom( struct window_class *class )
{
return class->base_atom;
}
client_ptr_t 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 unicode_str name = get_req_unicode_str();
atom_t atom, base_atom;
if (name.len)
{
atom = add_global_atom( NULL, &name );
if (!atom) return;
if (req->name_offset && req->name_offset < name.len / sizeof(WCHAR))
{
name.str += req->name_offset;
name.len -= req->name_offset * sizeof(WCHAR);
base_atom = add_global_atom( NULL, &name );
if (!base_atom)
{
release_global_atom( NULL, atom );
return;
}
}
else
{
base_atom = atom;
grab_global_atom( NULL, atom );
}
}
else
{
base_atom = atom = req->atom;
if (!grab_global_atom( NULL, atom )) return;
grab_global_atom( NULL, base_atom );
}
class = find_class( current->process, atom, req->instance );
if (class && !class->local == !req->local)
{
set_win32_error( ERROR_CLASS_ALREADY_EXISTS );
release_global_atom( NULL, atom );
release_global_atom( NULL, base_atom );
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 );
release_global_atom( NULL, atom );
release_global_atom( NULL, base_atom );
return;
}
if (!(class = create_class( current->process, req->extra, req->local )))
{
release_global_atom( NULL, atom );
release_global_atom( NULL, base_atom );
return;
}
class->atom = atom;
class->base_atom = base_atom;
class->instance = req->instance;
class->style = req->style;
class->win_extra = req->win_extra;
class->client_ptr = req->client_ptr;
reply->atom = atom;
}
/* destroy a window class */
DECL_HANDLER(destroy_class)
{
struct window_class *class;
struct unicode_str name = get_req_unicode_str();
atom_t atom = req->atom;
if (name.len) atom = find_global_atom( NULL, &name );
if (!(class = find_class( current->process, atom, req->instance )))
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;
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;
reply->base_atom = class->base_atom;
if (req->flags & SET_CLASS_ATOM)
{
if (!grab_global_atom( NULL, req->atom )) return;
release_global_atom( NULL, 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 );
}