Gnome Overlays
Gnome how to detect mouse events in an overlay window?
I recently wrote this posting about cross platform overlay windows:
My current Gnome implementation works, but with the following cavet: All mouse event are transparent to the window. That is, if the user presses a mouse button while over my overlay, the mouse press goes through my window and whatever is beneath it receives the mouse press. I am using a gtk_window_new(GTK_WINDOW_POPUP) as my overlay window class.
I'd like to revise my implementation such that mouse events are optionally captured by my window, but only when the pixel under the mouse is not transparent.
How can I do this with Gnome? On windows I can use the WM_HITTEST message, examine the pixel at the cursor position, and return HTTRANSPARENT to pass mouse events to the window below if the pixel is transparent. As an aside, I can also return HTCAPTION to allow the window to be dragged.
Whatever the solution is, I do not want my overlay to be activated or receive keyboard focus when a mouse button is pressed while inside my overlay window. I just want the option for mouse events to be gobbled by my overlay window (or perhaps processed by a mouse events callback for overlay) and not to always travel through it to whatever window is beneath the overlay.
procedure SplashScreenChanged(widget: PGtkWidget; old_screen: PGdkScreen;
userdata: GPointer); cdecl;
var
Screen: PGdkScreen;
Colormap: PGdkColormap;
begin
Screen := gtk_widget_get_screen(widget);
Colormap := gdk_screen_get_rgba_colormap(Screen);
gtk_widget_set_colormap(widget, Colormap);
end;
procedure Clip(Splash: TSplashCairo; Widget: PGtkWidget);
var
Window: PGdkWindow;
Bitmap: PGdkBitmap;
Bits: Integer;
begin
if Splash.FClipped then
Exit;
Splash.FClipped := True;
Window := gtk_widget_get_window(Widget);
Bits := 0;
Bitmap := gdk_bitmap_create_from_data(Widget.window, Pointer(@Bits), 1, 1);
gdk_window_input_shape_combine_mask(Window, Bitmap, 0, 0);
g_object_unref(Bitmap);
end;
function SplashExpose(widget: PGtkWidget; event: PGdkEventExpose;
userdata: GPointer): GBoolean; cdecl;
var
Splash: TSplashCairo absolute userdata;
BitmapSurface: TBitmapSurfaceCairo;
Surface: PCairoSurface;
Pattern: PCairoPattern;
Dest: PCairo;
begin
Clip(Splash, widget);
BitmapSurface := Splash.FBitmap.Surface as TBitmapSurfaceCairo;
if BitmapSurface.HandleAvailable then
begin
Surface := cairo_get_target(BitmapSurface.FCairo);
Pattern := cairo_pattern_create_for_surface(Surface);
Dest := gdk_cairo_create(widget.window);
cairo_set_operator(Dest, CAIRO_OPERATOR_SOURCE);
cairo_set_source(Dest, Pattern);
cairo_paint(Dest);
cairo_destroy(Dest);
cairo_pattern_destroy(Pattern);
end;
end;
constructor TSplashCairo.Create;
begin
inherited Create;
FBitmap := TBitmapCairo.Create;
FOpacity := $FF;
FWidget := gtk_window_new(GTK_WINDOW_POPUP);
gtk_widget_set_app_paintable(FWidget, True);
g_signal_connect(G_OBJECT(FWidget), 'expose-event',
G_CALLBACK(@SplashExpose), Pointer(Self));
g_signal_connect(G_OBJECT(FWidget), 'screen-changed',
G_CALLBACK(@SplashScreenChanged), nil);
SplashScreenChanged(FWidget, nil, nil);
end;
destructor TSplashCairo.Destroy;
begin
gtk_widget_destroy(FWidget);
inherited Destroy;
end;
function TSplashCairo.GetBitmap: IBitmap;
begin
Result := FBitmap;
end;
function TSplashCairo.GetOpacity: Byte;
begin
Result := FOpacity;
end;
procedure TSplashCairo.SetOpacity(Value: Byte);
begin
if Value <> FOpacity then
gtk_window_set_opacity(GTK_WINDOW(FWidget), Value / $FF);
FOpacity := Value;
end;
function TSplashCairo.GetVisible: Boolean;
begin
Result := FVisible;
end;
procedure TSplashCairo.SetVisible(Value: Boolean);
begin
Value := Value and (not FBitmap.Empty);
if Value <> FVisible then
begin
FVisible := Value;
if FVisible then
begin
gtk_window_resize(GTK_WINDOW(FWidget), FBitmap.Width, FBitmap.Height);
gtk_widget_show_all(FWidget);
end
else
gtk_widget_hide_all(FWidget);
end;
end;
function TSplashCairo.GetHandle: THandle;
begin
Result := THandle(FWidget);
end;
procedure TSplashCairo.Move(X, Y: Integer);
begin
gtk_window_move(GTK_WINDOW(FWidget), X, Y);
end;
procedure TSplashCairo.Update;
begin
if FBitmap.Empty then
SetVisible(False)
else if FVisible then
begin
gtk_window_resize(GTK_WINDOW(FWidget), FBitmap.Width, FBitmap.Height);
gtk_widget_queue_draw(FWidget);
end;
end;
function NewSplashCairo: ISplash;
begin
Result := TSplashCairo.Create;
end;