In windows, it's relatively easy:
- Create a window handle with name appname_apphandle.
(can be reduced to simply appname if only one instance will be running, see the thread about single instances for my suggestion about that)
- Use this handle to send a message to with WM_COPY.


On linux/Unix:
- Create a unix socket /tmp/appname.appid under /tmp or under ~/.appname/appid
 (same remark about single instance)

- Use this socket to send message to with standard socket functions.
I think that on Linux/unix you should use the X windows message passing, so that you can talk between programs running on different machines. I have had a stab at this in the past, and I attach a draft version. Though not shown in the attached, under MS windows I used RegisterWindowMessage('SpecialName'); and looked for this specific message in the appropriate wndproc (or it could be the overall message loop)
Colin
{ Provides a interprogram system that can be used on both Windows and X11 }
unit CompatMsg;
interface

uses {$IFDEF MSWINDOWS} Windows, {$ENDIF}
     {$IFDEF UNIX}
       XLib,X
       {$IFDEF KYLIX} Types, QGraphics, QControls, QForms, QDialogs, {$ENDIF}
       {$IFDEF FPC} , Graphics, gdk, {$ENDIF}
     {$ENDIF}
     SysUtils, Classes;

type

{ Typedef to help compatibility - the X Window/Windows Window Type}
{$IFDEF MSWINDOWS}
  TWnd = HWND;
{$ENDIF}
{$IFDEF UNIX}
  TWnd = {$IFDEF CLX_}Window{$ENDIF}{$IFDEF LCL_}TWindow{$ENDIF};
{$ENDIF}

  TOnCompatMsg = procedure (Sender: TObject; var Handled: Boolean) of object;

  TCompatMsg = class(TComponent)
  private
    FMessageName: string;
    FMessageID: {$IFDEF MSWINDOWS} UINT {$ENDIF} {$IFDEF UNIX} TAtom {$ENDIF};
    FOnResultReceived: TOnCompatMsg;
    FOnReceived: TOnCompatMsg;
    FStatus: Integer;
    procedure SetMessageName(const Value: string);
    procedure SetOnReceived(const Value: TOnCompatMsg);
    procedure SetOnResultReceived(const AValue: TOnCompatMsg);
    procedure SetStatus(const AValue: Integer);
  protected
    FInternalHook: Boolean;
    procedure DoOnReceived(var Handled: Boolean); virtual;
    procedure DoOnResultReceived(var Handled: Boolean); virtual;
  public
    procedure CheckHooked;
    destructor Destroy; override;
    property MessageID: {$IFDEF MSWINDOWS} UINT {$ENDIF} {$IFDEF UNIX} TAtom {$ENDIF} read FMessageID;
  published
    property Status: Integer read FStatus write SetStatus;
    property MessageName: string read FMessageName write SetMessageName;
    property OnReceived: TOnCompatMsg read FOnReceived write SetOnReceived;
    property OnResultReceived: TOnCompatMsg read FOnResultReceived write SetOnResultReceived;
  end;

{$IFDEF UNIX}
function RootWindow:TWnd;
{ Handler for clipboard when type = XA_PIXMAP }
function LoadBitmapFromClipboard(Bitmap: TBitmap):Boolean;
function X11Display:PDisplay;
{$ENDIF}

function MainWindow:TWnd;

implementation

{$IFDEF MSWINDOWS}
uses Forms;

function MainWindow: TWnd;
begin
  if Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
    Result := Application.MainForm.Handle
  else
    Result := 0;
end;
{$ENDIF}

{$IFDEF UNIX}
uses
  Clipbrd,
  {$IFDEF KYLIX} Qt, Libc; {$ENDIF}
  {$IFDEF LCL_} GTKProc, GTKDef, gtk, glib, Xatom, Forms; {$ENDIF}
{$ENDIF}

var
  CompatMsgList: TList;

{$IFDEF UNIX}
{$IFDEF LCL_}
function X11Display:PDisplay;
begin
  Result := gdk_display;
end;
{$ENDIF}
{$IFDEF LCL_}
function MainWindow: TWnd;
begin
  if Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
    Result := PGdkWindowPrivate(PGTKWidget(Application.MainForm.Handle)^.Window)^.xwindow
  else
    Result := 0;
end;
{$ENDIF}
{$IFDEF CLX_}
function X11Display:???what is the type
begin
  Result := QtDisplay;
end;
function MainWindow: TWindow;
begin
  Result := QWidget_winId(Application.MainForm.Handle);
end;
{$ENDIF}
var
  RootWindowSave: TWnd;

function RootWindow:TWnd;
begin
  if RootWindowSave = 0 then
    RootWindowSave := XDefaultRootWindow(gdk_display);
  Result := RootWindowSave;
end;
{$ENDIF}

{$IFDEF KYLIX}
{$IFNDEF DFS_KYLIX_3_UP}
{ Kylix 1/2 require this }
type
  X11EventFilter = function(XEvent: PXEvent): Boolean; cdecl;
// bind using the mangled name export from libqt.so, determined using
// "objdump --dynamic-syms libqt.so | grep set_x11_event_filter". YMMV.

function SetX11EventFilter(Filter: X11EventFilter): X11EventFilter; cdecl;
  external { 'libqt.so' } '' name QtNamePrefix + 'qt_set_x11_event_filter__FPFP7_XEvent_i';
{$ENDIF}
var
  OldFilter: X11EventFilter = nil;

function CompatX11EventFilter(Event: PXEvent): Boolean; cdecl;
var
  i: Integer;
begin
  // Return False to continue processing, non-zero to prevent.
  Result := False;
  { WriteLn('Event: ', Event^.xtype); }
  if (Event^.xtype = PropertyNotify) and (Event^.xproperty.state <> PropertyDelete) then begin
    for i := 0 to CompatMsgList.Count - 1 do
      if TCompatMsg(CompatMsgList[i]).MessageID = Event^.xproperty.atom then begin
         TCompatMsg(CompatMsgList[i]).DoOnReceived(Result);
         if Result then Exit;
      end;
  end else if Event^.xtype = ClientMessage then begin
    for i := 0 to CompatMsgList.Count - 1 do
      if TCompatMsg(CompatMsgList[i]).MessageID = Event^.xclient.message_type then begin
         TCompatMsg(CompatMsgList[i]).DoOnReceived(Result);
         if Result then Exit;
      end;
  end;
  if Assigned(OldFilter) then
    Result := OldFilter(Event);
end;
{$ENDIF}

{$IFDEF LCL_}
function ClientMessageHook(xevent:PGdkXEvent; event:PGdkEvent; data:gpointer):TGdkFilterReturn;cdecl;
var
  i: Integer;
  Handled: Boolean;
begin
  // WriteLn('Event: ', PGdkEventAny(xevent)^.thetype, ':', PXEvent(xevent)^.xclient.message_type, ' w:', PXEvent(xevent)^.xclient.window );
  Handled := False;
  if (PGdkEventAny(xevent)^.thetype = ClientMessage) and (Xlib.PXEvent(xevent)^.xclient.window = MainWindow)
     and Assigned(CompatMsgList) then
    { Must search through list as there is no procedure to remove a filter,
      and also to allow for multiple hooks on same message }
    for i := 0 to CompatMsgList.Count - 1 do
      with TCompatMsg(CompatMsgList.Items[i]) do
        if MessageID = Xlib.PXEvent(xevent)^.xclient.message_type then begin
          DoOnReceived(Handled);
          if Handled then
            Break;
        end;
   if Handled then
     Result := GDK_FILTER_REMOVE
   else
     Result := GDK_FILTER_CONTINUE; { or possibly GDK_FILTER_TRANSLATE ? }
end;
{$ENDIF}

{$IFDEF LCL_}
var
  AtomsMonitored: array of TAtom;
{$ENDIF}

procedure Hook(CompatMsg: TCompatMsg);
{$IFDEF LCL_}
var
  i: Integer;
{$ENDIF}
begin
  if not Assigned(CompatMsgList) then begin
    CompatMsgList := TList.Create;
    {$IFDEF KYLIX}
    OldFilter := {$IFDEF DFS_KYLIX_3_UP}Application.{$ENDIF}
                   SetX11EventFilter(CompatX11EventFilter);
    {$ENDIF}
    // WriteLn(MainWindow);
  end;
  if CompatMsgList.IndexOf(CompatMsg) < 0 then
    CompatMsgList.Add(CompatMsg);
  {$IFDEF LCL_}
  { This logic is required because there is no gdk_remove_client_message function,
    so we make sure we call the add function when absolutely necessary }
  for i := 0 to High(AtomsMonitored) do
    if AtomsMonitored[i] = CompatMsg.MessageID then
      Exit;
  SetLength(AtomsMonitored, Length(AtomsMonitored)+1);
  AtomsMonitored[High(AtomsMonitored)] := CompatMsg.MessageID;
  gdk_add_client_message_filter(CompatMsg.MessageID, @ClientMessageHook, nil);
  {$ENDIF}
end;

procedure UnHookAll;
begin
  if Assigned(CompatMsgList) then begin
    FreeAndNil(CompatMsgList);
    {$IFDEF KYLIX}
    {$IFDEF DFS_KYLIX_3_UP}
      OldFilter := Application.{$ENDIF} { Assignment seems to be required to keep compiler happy }
        SetX11EventFilter(OldFilter);
    {$ENDIF}
  end;
end;

procedure UnHook(CompatMsg: TCompatMsg);
begin
  if Assigned(CompatMsgList) and (CompatMsgList.IndexOf(CompatMsg) >= 0) then begin
    CompatMsgList.Remove(CompatMsg);
    CompatMsgList.Pack;
    if CompatMsgList.Count = 0 then
      UnHookAll;
  end;
end;

{ TCompatMsg }

destructor TCompatMsg.Destroy;
begin
  FInternalHook := False;
  OnReceived := nil;
  OnResultReceived := nil;
  inherited;
end;

procedure TCompatMsg.DoOnReceived(var Handled: Boolean);
begin
  if Assigned(FOnReceived) then begin
    {$IFDEF UNIX}
    try
      Status := -1; { Processing }
      FOnReceived(Self, Handled);
      if Status = -1 then
        Status := Ord(Handled);
    except
      Status := -2; { Flag crash }
      Application.HandleException(Self);
    end;
    {$ENDIF}
  end;
end;

procedure TCompatMsg.DoOnResultReceived(var Handled: Boolean);
begin
  if Assigned(FOnResultReceived) then
    FOnResultReceived(Self, Handled);
end;

procedure TCompatMsg.CheckHooked;
begin
  if not (csDesigning in ComponentState) then begin
    if not Assigned(OnReceived) and not Assigned(OnResultReceived) and not (FInternalHook) then
      UnHook(Self)
    else
      Hook(Self);
  end;
end;

procedure TCompatMsg.SetMessageName(const Value: string);
begin
  FMessageName := Value;
  if not (csDesigning in ComponentState) then begin
    {$IFDEF MSWINDOWS}
    FMessageID := RegisterWindowMessage(PChar(Value));
    {$ENDIF}
    {$IFDEF UNIX}
    FMessageID := XInternAtom(gdk_display,PChar(Value), False);
    {$ENDIF}
  end;
end;

procedure TCompatMsg.SetOnReceived(const Value: TOnCompatMsg);
begin
  FOnReceived := Value;
  CheckHooked;
end;

procedure TCompatMsg.SetOnResultReceived(const AValue: TOnCompatMsg);
begin
  FOnResultReceived := AValue;
  CheckHooked;
end;

procedure TCompatMsg.SetStatus(const AValue: Integer);
begin
  if (FStatus=AValue) or (MainWindow =0) or (MessageID = 0) then exit;
  FStatus:=AValue;
  {$IFDEF UNIX}
  XChangeProperty(gdk_display, MainWindow, MessageID, XA_INTEGER, 32, PropModeReplace, @FStatus, 1);
  XFlush(gdk_display);
  {$ENDIF}
end;

initialization

finalization
  UnHookAll;
end.

Reply via email to