Ok, The problem was about giving enougth time for the window to show at
least once. Now it works 100% of the time on KDE, but for it to happen I
had to move that Sleep(80) to CreateForm() .
On the other hand, on IceWM instead of my icon, I see some random colors
painted and it doesn't respond to mouse events.
On Gnome I get a 1x1 pixels Tray Icon.
Any idea???
I am attaching the new source.
thanks,
Felipe
{
wsgtk2trayicon.pas
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program 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. *
* *
*****************************************************************************
Authors: Felipe Monteiro de Carvalho and Andrew Haines
Gtk2 specific code.
}
unit wsgtk2trayicon;
{$ifdef FPC}
{$mode delphi}{$H+}
{$endif}
{$PACKRECORDS C}
interface
uses
Graphics, Classes, ExtCtrls, SysUtils, Forms, Controls, Dialogs,
Menus, wscommontrayicon, x, xlib, xutil, gtk2, gdk2, gdk2x;
type
{ TWidgetTrayIcon }
TWidgetTrayIcon = class(TCustomWidgetTrayIcon)
private
fDisplay: PDisplay;
fWindow: TWindow;
fScreen: PScreen;
fScreenID: longint;
fTrayParent: TWindow;
fOwner: TComponent;
GtkForm: TForm;
fEmbedded: Boolean;
fMsgCount: Integer;
procedure SetEmbedded;
function Send_Message(window: TWindow; msg: Integer; data1, data2, data3: Integer): boolean;
procedure SetMinSize(AWidth, AHeight: Integer);
procedure PaintForm(Sender: TObject);
procedure CreateForm(id: Integer);
procedure RemoveForm(id: Integer);
function GetCanvas: TCanvas;
protected
public
function Hide: Boolean; override;
function Show: Boolean; override;
property Canvas: TCanvas read GetCanvas;
procedure InternalUpdate; override;
published
end;
const
SYSTEM_TRAY_REQUEST_DOCK = 0;
SYSTEM_TRAY_BEGIN_MESSAGE = 1;
SYSTEM_TRAY_CANCEL_MESSAGE = 2;
implementation
type
PX11GdkDrawable = ^TX11GdkDrawable;
TX11GdkDrawable = record
parent_instance: TGdkWindow;
wrapper: PGdkDrawable;
colormap: PGdkColorMap;
xid:x.TWindow;
end;
{*******************************************************************
* TempX11ErrorHandler ()
*
* DESCRIPTION: Temp ErrorHandler
*
* PARAMETERS: ?
*
* RETURNS: ?
*
*******************************************************************}
function TempX11ErrorHandler(Display:PDisplay; ErrorEv:PXErrorEvent):longint;cdecl;
begin
WriteLn('Error: ' + IntToStr(ErrorEv^.error_code));
end;
{ TWidgetTrayIcon }
{*******************************************************************
* TWidgetTrayIcon.SetEmbedded ()
*
* DESCRIPTION:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.SetEmbedded;
var
old_error: TXErrorHandler;
buf: array [0..32] of char;
selection_atom : TAtom;
begin
old_error := XSetErrorHandler(@TempX11ErrorHandler);
xsync(fdisplay,true);
buf := PChar('_NET_SYSTEM_TRAY_S' + IntToStr(fScreenID));
selection_atom := XInternAtom(fDisplay, buf, false);
XGrabServer(fDisplay);
fTrayParent := XGetSelectionOwner(fDisplay, selection_atom);
if fTrayParent <> None then
begin
XSelectInput(fDisplay, fTrayParent, StructureNotifyMask);
end;
XUngrabServer(fDisplay);
XFlush(fDisplay);
if fTrayParent <> None then
Send_Message(fTrayParent, SYSTEM_TRAY_REQUEST_DOCK, fWindow, 0, 0);
XSetErrorHandler(old_error);
end;
{*******************************************************************
* TWidgetTrayIcon.Send_Message ()
*
* DESCRIPTION: Sends a message to the X client
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
function TWidgetTrayIcon.Send_Message(window: TWindow; msg: Integer; data1, data2, data3: Integer): boolean;
var
Ev: TXEvent;
fmt: Integer;
begin
FillChar(Ev, SizeOf(TXEvent), $0);
ev.xclient._type := ClientMessage;
ev.xclient.window := window;
ev.xclient.message_type := XInternAtom (fDisplay, '_NET_SYSTEM_TRAY_OPCODE', False );
ev.xclient.format := 32;
ev.xclient.data.l[0] := CurrentTime;
ev.xclient.data.l[1] := msg;
ev.xclient.data.l[2] := data1;
ev.xclient.data.l[3] := data2;
ev.xclient.data.l[4] := data3;
XSendEvent(fDisplay, fTrayParent, False, NoEventMask, @ev);
XSync(fDisplay, False);
Result := false;//(untrap_errors() = 0);
end;
{*******************************************************************
* TWidgetTrayIcon.CreateForm ()
*
* DESCRIPTION:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.CreateForm(id: Integer);
var
Widget: PGtkWidget;
begin
GtkForm := TForm.Create(nil);
fEmbedded := False;
//fWindow := GDK_WINDOW_XWINDOW (Pointer(PGtkWidget(GtkForm.Handle)^.window));
//SHowMessage(IntToStr(Integer(fWindow)));
//GtkForm.Parent := TWinConTrol(fOwner);
// GtkForm.WindowState := wsMinimized;
GtkForm.BorderStyle := bsNone; //without this gnome will make a 1 pixel wide window!
//GtkForm.Canvas.AutoRedraw := True; //not working :(
{*******************************************************************
* The following code is needed because some things aparently don't
* get fully initialized until the form is visible at least once!
*
* This is Gtk related NOT LCL related.
*
* Without the this code, sometimes the tray icon will work correctly,
* and on others only a grey space will be reserved on the tray and
* the window of the icon will be visible on the desktop
*******************************************************************}
GtkForm.Show;
GtkForm.Width := 22;
GtkForm.Height := 22;
Application.ProcessMessages;
Sleep(80);
Application.ProcessMessages;
fDisplay := GDK_WINDOW_XDISPLAY(Pointer(PGtkWidget(GtkForm.Handle)^.window));
fWindow := GDK_WINDOW_XWINDOW(Pointer(PGtkWidget(GtkForm.Handle)^.window));
fScreen := XDefaultScreenOfDisplay(fDisplay); // get the screen
fScreenID := XScreenNumberOfScreen(fScreen); // and it's number
end;
{*******************************************************************
* TWidgetTrayIcon.RemoveForm ()
*
* DESCRIPTION:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.RemoveForm(id: Integer);
begin
GtkForm.Free;
end;
{*******************************************************************
* TWidgetTrayIcon.GetCanvas ()
*
* DESCRIPTION:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
function TWidgetTrayIcon.GetCanvas: TCanvas;
begin
Result := GtkForm.Canvas;
end;
{*******************************************************************
* TWidgetTrayIcon.Hide ()
*
* DESCRIPTION: Hides the main tray icon of the program
*
* PARAMETERS: None
*
* RETURNS: True if sucessfull, otherwise False
*
*******************************************************************}
function TWidgetTrayIcon.Hide: Boolean;
begin
Result := False;
if not vVisible then Exit;
RemoveForm(0);
vVisible := False;
Result := True;
end;
{*******************************************************************
* TWidgetTrayIcon.Show ()
*
* DESCRIPTION: Shows the main tray icon of the program
*
* PARAMETERS: None
*
* RETURNS: True if sucessfull, otherwise False
*
*******************************************************************}
function TWidgetTrayIcon.Show: Boolean;
begin
Result := False;
if vVisible then Exit;
CreateForm(0);
SetEmbedded;
GtkForm.Width := 22; //needed for gnome
GtkForm.Height := 22;
SetMinSize(Icon.Width, Icon.Height);
GtkForm.OnMouseDown := Self.OnMouseDown;
GtkForm.OnMouseMove := Self.OnMouseMove;
GtkForm.OnMouseUp := Self.OnMouseUp;
GtkForm.OnClick := Self.OnClick;
GtkForm.OnPaint := PaintForm;
GtkForm.PopupMenu := Self.PopUpMenu;
fEmbedded := True;
vVisible := True;
Result := True;
end;
{*******************************************************************
* TWidgetTrayIcon.SetMinSize ()
*
* DESCRIPTION: Attemps to avoid problems on Gnome
*
* PARAMETERS:
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.SetMinSize(AWidth, AHeight: Integer);
var
size_hints: TXSizeHints;
begin
FillChar(size_hints, SizeOf(TXSizeHints), $0);
size_hints.flags := PSize or PMinSize or PMaxSize;
size_hints.width := AWidth;
size_hints.height := AHeight;
size_hints.min_width := AWidth;
size_hints.max_width := 100;
size_hints.min_height := AHeight;
size_hints.max_height := 100;
XSetStandardProperties(fDisplay, fWindow, nil, nil, None, nil, 0, @size_hints);
end;
{*******************************************************************
* TWidgetTrayIcon.PaintForm ()
*
* DESCRIPTION: Paint method of the Icon Window
*
* PARAMETERS: Sender of the event
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.PaintForm(Sender: TObject);
begin
if ShowIcon then GtkForm.Canvas.Draw(0, 0, Icon);
if Assigned(OnPaint) then OnPaint(Self);
end;
{*******************************************************************
* TWidgetTrayIcon.InternalUpdate ()
*
* DESCRIPTION: Makes modifications to the Icon while running
* i.e. without hiding it and showing again
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
*******************************************************************}
procedure TWidgetTrayIcon.InternalUpdate;
begin
if Assigned(GtkForm) then GtkForm.PopupMenu := Self.PopUpMenu;
end;
end.