riched20: Always write out the color table.
[wine] / server / window.c
index c8c075f..e141f93 100644 (file)
@@ -93,10 +93,16 @@ struct window
     char             extra_bytes[1];  /* extra bytes storage */
 };
 
-#define PAINT_INTERNAL      0x01  /* internal WM_PAINT pending */
-#define PAINT_ERASE         0x02  /* needs WM_ERASEBKGND */
-#define PAINT_NONCLIENT     0x04  /* needs WM_NCPAINT */
-#define PAINT_DELAYED_ERASE 0x08  /* still needs erase after WM_ERASEBKGND */
+/* flags that can be set by the client */
+#define PAINT_HAS_SURFACE        SET_WINPOS_PAINT_SURFACE
+#define PAINT_HAS_PIXEL_FORMAT   SET_WINPOS_PIXEL_FORMAT
+#define PAINT_CLIENT_FLAGS       (PAINT_HAS_SURFACE | PAINT_HAS_PIXEL_FORMAT)
+/* flags only manipulated by the server */
+#define PAINT_INTERNAL           0x0010  /* internal WM_PAINT pending */
+#define PAINT_ERASE              0x0020  /* needs WM_ERASEBKGND */
+#define PAINT_NONCLIENT          0x0040  /* needs WM_NCPAINT */
+#define PAINT_DELAYED_ERASE      0x0080  /* still needs erase after WM_ERASEBKGND */
+#define PAINT_PIXEL_FORMAT_CHILD 0x0100  /* at least one child has a custom pixel format */
 
 /* growable array of user handles */
 struct user_handle_array
@@ -160,6 +166,14 @@ static inline struct window *get_last_child( struct window *win )
     return ptr ? LIST_ENTRY( ptr, struct window, entry ) : NULL;
 }
 
+/* set the PAINT_PIXEL_FORMAT_CHILD flag on all the parents */
+/* note: we never reset the flag, it's just a heuristic */
+static inline void update_pixel_format_flags( struct window *win )
+{
+    for (win = win->parent; win && win->parent; win = win->parent)
+        win->paint_flags |= PAINT_PIXEL_FORMAT_CHILD;
+}
+
 /* link a window at the right place in the siblings list */
 static void link_window( struct window *win, struct window *previous )
 {
@@ -239,6 +253,9 @@ static int set_parent_window( struct window *win, struct window *parent )
         /* top-level, attach the two threads */
         if (parent->thread && parent->thread != win->thread && !is_desktop_window(parent))
             attach_thread_input( win->thread, parent->thread );
+
+        if (win->paint_flags & (PAINT_HAS_PIXEL_FORMAT | PAINT_PIXEL_FORMAT_CHILD))
+            update_pixel_format_flags( win );
     }
     else  /* move it to parent unlinked list */
     {
@@ -394,11 +411,20 @@ struct process *get_top_window_owner( struct desktop *desktop )
     return win->thread->process;
 }
 
-/* attempt to close the desktop window when the last process using it is gone */
-void close_desktop_window( struct desktop *desktop )
+/* get the top window size of a given desktop */
+void get_top_window_rectangle( struct desktop *desktop, rectangle_t *rect )
 {
     struct window *win = desktop->top_window;
-    if (win && win->thread) post_message( win->handle, WM_CLOSE, 0, 0 );
+    if (!win) rect->left = rect->top = rect->right = rect->bottom = 0;
+    else *rect = win->window_rect;
+}
+
+/* post a message to the desktop window */
+void post_desktop_message( struct desktop *desktop, unsigned int message,
+                           lparam_t wparam, lparam_t lparam )
+{
+    struct window *win = desktop->top_window;
+    if (win && win->thread) post_message( win->handle, message, wparam, lparam );
 }
 
 /* create a new window structure (note: the window is not linked in the window tree) */
@@ -569,9 +595,13 @@ int make_window_active( user_handle_t window )
 
     if (!win) return 0;
 
-    /* set last active for window and its owner */
-    win->last_active = win->handle;
-    if ((owner = get_user_object( win->owner, USER_WINDOW ))) owner->last_active = win->handle;
+    /* set last active for window and its owners */
+    owner = win;
+    while (owner)
+    {
+        owner->last_active = win->handle;
+        owner = get_user_object( owner->owner, USER_WINDOW );
+    }
     return 1;
 }
 
@@ -884,17 +914,6 @@ static struct region *clip_children( struct window *parent, struct window *last,
 }
 
 
-/* compute the intersection of two rectangles; return 0 if the result is empty */
-static inline int intersect_rect( rectangle_t *dst, const rectangle_t *src1, const rectangle_t *src2 )
-{
-    dst->left   = max( src1->left, src2->left );
-    dst->top    = max( src1->top, src2->top );
-    dst->right  = min( src1->right, src2->right );
-    dst->bottom = min( src1->bottom, src2->bottom );
-    return (dst->left < dst->right && dst->top < dst->bottom);
-}
-
-
 /* offset the coordinates of a rectangle */
 static inline void offset_rect( rectangle_t *rect, int offset_x, int offset_y )
 {
@@ -918,7 +937,8 @@ static void set_region_client_rect( struct region *region, struct window *win )
 /* get the top-level window to clip against for a given window */
 static inline struct window *get_top_clipping_window( struct window *win )
 {
-    while (win->parent && !is_desktop_window(win->parent)) win = win->parent;
+    while (!(win->paint_flags & PAINT_HAS_SURFACE) && win->parent && !is_desktop_window(win->parent))
+        win = win->parent;
     return win;
 }
 
@@ -1010,6 +1030,84 @@ error:
 }
 
 
+/* clip all children with a custom pixel format out of the visible region */
+static struct region *clip_pixel_format_children( struct window *parent, struct region *parent_clip,
+                                                  struct region *region, int offset_x, int offset_y )
+{
+    struct window *ptr;
+    struct region *clip = create_empty_region();
+
+    if (!clip) return NULL;
+
+    LIST_FOR_EACH_ENTRY_REV( ptr, &parent->children, struct window, entry )
+    {
+        if (!(ptr->style & WS_VISIBLE)) continue;
+        if (ptr->ex_style & WS_EX_TRANSPARENT) continue;
+
+        /* add the visible rect */
+        set_region_rect( clip, &ptr->visible_rect );
+        if (ptr->win_region && !intersect_window_region( clip, ptr )) break;
+        offset_region( clip, offset_x, offset_y );
+        if (!intersect_region( clip, clip, parent_clip )) break;
+        if (!union_region( region, region, clip )) break;
+        if (!(ptr->paint_flags & (PAINT_HAS_PIXEL_FORMAT | PAINT_PIXEL_FORMAT_CHILD))) continue;
+
+        /* subtract the client rect if it uses a custom pixel format */
+        set_region_rect( clip, &ptr->client_rect );
+        if (ptr->win_region && !intersect_window_region( clip, ptr )) break;
+        offset_region( clip, offset_x, offset_y );
+        if (!intersect_region( clip, clip, parent_clip )) break;
+        if ((ptr->paint_flags & PAINT_HAS_PIXEL_FORMAT) && !subtract_region( region, region, clip ))
+            break;
+
+        if (!clip_pixel_format_children( ptr, clip, region, offset_x + ptr->client_rect.left,
+                                         offset_y + ptr->client_rect.top ))
+            break;
+    }
+    free_region( clip );
+    return region;
+}
+
+
+/* compute the visible surface region of a window, in parent coordinates */
+static struct region *get_surface_region( struct window *win )
+{
+    struct region *region, *clip;
+    int offset_x, offset_y;
+
+    /* create a region relative to the window itself */
+
+    if (!(region = create_empty_region())) return NULL;
+    if (!(clip = create_empty_region())) goto error;
+    set_region_rect( region, &win->visible_rect );
+    if (win->win_region && !intersect_window_region( region, win )) goto error;
+    set_region_rect( clip, &win->client_rect );
+    if (win->win_region && !intersect_window_region( clip, win )) goto error;
+
+    if ((win->paint_flags & PAINT_HAS_PIXEL_FORMAT) && !subtract_region( region, region, clip ))
+        goto error;
+
+    /* clip children */
+
+    if (!is_desktop_window(win))
+    {
+        offset_x = win->client_rect.left;
+        offset_y = win->client_rect.top;
+    }
+    else offset_x = offset_y = 0;
+
+    if (!clip_pixel_format_children( win, clip, region, offset_x, offset_y )) goto error;
+
+    free_region( clip );
+    return region;
+
+error:
+    if (clip) free_region( clip );
+    free_region( region );
+    return NULL;
+}
+
+
 /* get the window class of a window */
 struct window_class* get_window_class( user_handle_t window )
 {
@@ -1486,7 +1584,7 @@ static struct region *expose_window( struct window *win, const rectangle_t *old_
         }
     }
 
-    if (win->parent)
+    if (win->parent && !is_desktop_window( win->parent ))
     {
         /* make it relative to the old window pos for subtracting */
         offset_region( new_vis_rgn, win->window_rect.left - old_window_rect->left,
@@ -1551,6 +1649,9 @@ static void set_window_pos( struct window *win, struct window *previous,
         }
     }
 
+    /* reset cursor clip rectangle when the desktop changes size */
+    if (win == win->desktop->top_window) win->desktop->cursor.clip = *window_rect;
+
     /* if the window is not visible, everything is easy */
     if (!visible) return;
 
@@ -1640,6 +1741,13 @@ static void set_window_pos( struct window *win, struct window *previous,
             if (tmp)
             {
                 set_region_rect( tmp, &valid_rects[0] );
+                /* subtract update region since invalid parts of the valid rect won't be copied */
+                if (win->update_region)
+                {
+                    offset_region( tmp, -window_rect->left, -window_rect->top );
+                    subtract_region( tmp, tmp, win->update_region );
+                    offset_region( tmp, window_rect->left, window_rect->top );
+                }
                 if (subtract_region( tmp, win_rgn, tmp )) win_rgn = tmp;
                 else free_region( tmp );
             }
@@ -1722,6 +1830,7 @@ void destroy_window( struct window *win )
     if (win == shell_listview) shell_listview = NULL;
     if (win == progman_window) progman_window = NULL;
     if (win == taskman_window) taskman_window = NULL;
+    free_hotkeys( win->desktop, win->handle );
     free_user_handle( win->handle );
     destroy_properties( win );
     list_remove( &win->entry );
@@ -2080,8 +2189,8 @@ DECL_HANDLER(set_window_pos)
 {
     rectangle_t window_rect, client_rect, visible_rect;
     struct window *previous = NULL;
-    struct window *win = get_window( req->handle );
-    unsigned int flags = req->flags;
+    struct window *top, *win = get_window( req->handle );
+    unsigned int flags = req->swp_flags;
 
     if (!win) return;
     if (!win->parent) flags |= SWP_NOZORDER;  /* no Z order for the desktop */
@@ -2115,6 +2224,9 @@ DECL_HANDLER(set_window_pos)
         if (previous == win) flags |= SWP_NOZORDER;  /* nothing to do */
     }
 
+    /* windows that use UpdateLayeredWindow don't trigger repaints */
+    if ((win->ex_style & WS_EX_LAYERED) && !win->is_layered) flags |= SWP_NOREDRAW;
+
     /* window rectangle must be ordered properly */
     if (req->window.right < req->window.left || req->window.bottom < req->window.top)
     {
@@ -2133,14 +2245,17 @@ DECL_HANDLER(set_window_pos)
         mirror_rect( &win->parent->client_rect, &client_rect );
     }
 
+    win->paint_flags = (win->paint_flags & ~PAINT_CLIENT_FLAGS) | (req->paint_flags & PAINT_CLIENT_FLAGS);
+    if (win->paint_flags & PAINT_HAS_PIXEL_FORMAT) update_pixel_format_flags( win );
+
     if (get_req_data_size() >= 3 * sizeof(rectangle_t))
     {
         rectangle_t valid_rects[2];
         memcpy( valid_rects, (const rectangle_t *)get_req_data() + 1, 2 * sizeof(rectangle_t) );
-        if (win->ex_style & WS_EX_LAYOUTRTL)
+        if (win->parent && win->parent->ex_style & WS_EX_LAYOUTRTL)
         {
-            mirror_rect( &win->client_rect, &valid_rects[0] );
-            mirror_rect( &win->client_rect, &valid_rects[1] );
+            mirror_rect( &win->parent->client_rect, &valid_rects[0] );
+            mirror_rect( &win->parent->client_rect, &valid_rects[1] );
         }
         set_window_pos( win, previous, flags, &window_rect, &client_rect, &visible_rect, valid_rects );
     }
@@ -2148,6 +2263,12 @@ DECL_HANDLER(set_window_pos)
 
     reply->new_style = win->style;
     reply->new_ex_style = win->ex_style;
+
+    top = get_top_clipping_window( win );
+    if (is_visible( top ) &&
+        (top->paint_flags & PAINT_HAS_SURFACE) &&
+        (top->paint_flags & (PAINT_HAS_PIXEL_FORMAT | PAINT_PIXEL_FORMAT_CHILD)))
+        reply->surface_win = top->handle;
 }
 
 
@@ -2298,7 +2419,7 @@ DECL_HANDLER(get_visible_region)
         if (data) set_reply_data_ptr( data, reply->total_size );
     }
     reply->top_win  = top->handle;
-    reply->top_rect = (top == win && (req->flags & DCX_WINDOW)) ? top->visible_rect : top->client_rect;
+    reply->top_rect = top->visible_rect;
 
     if (!is_desktop_window(win))
     {
@@ -2313,6 +2434,27 @@ DECL_HANDLER(get_visible_region)
         reply->win_rect.right  = win->client_rect.right - win->client_rect.left;
         reply->win_rect.bottom = win->client_rect.bottom - win->client_rect.top;
     }
+    reply->paint_flags = win->paint_flags & PAINT_CLIENT_FLAGS;
+}
+
+
+/* get the surface visible region of a window */
+DECL_HANDLER(get_surface_region)
+{
+    struct region *region;
+    struct window *win = get_window( req->window );
+
+    if (!win || !is_visible( win )) return;
+
+    if ((region = get_surface_region( win )))
+    {
+        rectangle_t *data;
+        if (win->parent) map_win_region_to_screen( win->parent, region );
+        data = get_region_data_and_free( region, get_reply_max_size(), &reply->total_size );
+        if (data) set_reply_data_ptr( data, reply->total_size );
+    }
+    reply->visible_rect = win->visible_rect;
+    if (win->parent) client_to_screen_rect( win->parent, &reply->visible_rect );
 }
 
 
@@ -2450,8 +2592,14 @@ DECL_HANDLER(update_window_zorder)
         if (ptr == win) break;
         if (!(ptr->style & WS_VISIBLE)) continue;
         if (ptr->ex_style & WS_EX_TRANSPARENT) continue;
+        if (ptr->is_layered && (ptr->layered_flags & LWA_COLORKEY)) continue;
         if (!intersect_rect( &tmp, &ptr->visible_rect, &rect )) continue;
-        if (ptr->win_region && !rect_in_region( ptr->win_region, &rect )) continue;
+        if (ptr->win_region)
+        {
+            tmp = rect;
+            offset_rect( &tmp, -ptr->window_rect.left, -ptr->window_rect.top );
+            if (!rect_in_region( ptr->win_region, &tmp )) continue;
+        }
         /* found a window obscuring the rectangle, now move win above this one */
         /* making sure to not violate the topmost rule */
         if (!(ptr->ex_style & WS_EX_TOPMOST) || (win->ex_style & WS_EX_TOPMOST))