This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new 5cb5aa5  fix(desktop): hand a second launch to the tray already running
5cb5aa5 is described below

commit 5cb5aa58e2b8c1b84f21d93b1a496438b6e1dc95
Author: Yang Luo <[email protected]>
AuthorDate: Sat Sep 5 00:41:17 2026 +0800

    fix(desktop): hand a second launch to the tray already running
---
 .gitignore          |   3 ++
 desktop/instance.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 desktop/main.go     |   8 ++++
 desktop/tray.go     |   1 +
 4 files changed, 138 insertions(+)

diff --git a/.gitignore b/.gitignore
index 5884cba..f6733c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,9 @@
 casbin-gateway
 casbin-gateway-desktop
 
+# Written next to the binaries at run time, so a second launch finds the tray
+.desktop-instance
+
 # Written next to the binaries at run time, for the window and the shortcuts
 casbin-gateway.ico
 casbin-gateway.icns
diff --git a/desktop/instance.go b/desktop/instance.go
new file mode 100644
index 0000000..3bd703d
--- /dev/null
+++ b/desktop/instance.go
@@ -0,0 +1,126 @@
+// Copyright 2026 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "net"
+       "os"
+       "path/filepath"
+       "strconv"
+       "strings"
+       "time"
+)
+
+const (
+       showRequest  = "show"
+       showReply    = "ok"
+       instanceWait = 2 * time.Second
+)
+
+// instanceMarker records the loopback port the running tray listens on. The
+// port is ephemeral rather than fixed, because a fixed one would sooner or
+// later be a port something else on the machine wanted - a second Gateway home
+// keeps a marker of its own next to its own data.
+func instanceMarker() string {
+       return filepath.Join(gatewayHome(), ".desktop-instance")
+}
+
+// raiseRunningInstance hands this launch to the tray that is already running.
+// Clicking the shortcut of an app that is open means "show me the window", so
+// what would have been a second tray icon and a second window becomes the one
+// that is there, raised. It reports whether a tray took the launch.
+func raiseRunningInstance() bool {
+       data, err := os.ReadFile(instanceMarker())
+       if err != nil {
+               return false
+       }
+       port, err := strconv.Atoi(strings.TrimSpace(string(data)))
+       if err != nil || port <= 0 {
+               return false
+       }
+
+       // A marker that outlived the tray which wrote it leaves the launch to 
run
+       // the tray itself, which is also what overwrites the marker.
+       conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 
instanceWait)
+       if err != nil {
+               return false
+       }
+       defer conn.Close()
+
+       _ = conn.SetDeadline(time.Now().Add(instanceWait))
+       if _, err = io.WriteString(conn, showRequest+"\n"); err != nil {
+               return false
+       }
+
+       // Whatever holds that port now may not be a Gateway tray, so the 
launch is
+       // only given up once one has answered.
+       reply, err := bufio.NewReader(conn).ReadString('\n')
+       return err == nil && strings.TrimSpace(reply) == showReply
+}
+
+// holdInstance opens the socket later launches look for. Failing to leaves the
+// desktop as it was before any of this: a second launch is a second tray.
+func holdInstance() {
+       listener, err := net.Listen("tcp", "127.0.0.1:0")
+       if err != nil {
+               fmt.Fprintln(os.Stderr, "casbin-gateway-desktop:", err)
+               return
+       }
+
+       address, ok := listener.Addr().(*net.TCPAddr)
+       if !ok {
+               listener.Close()
+               return
+       }
+       if err = os.WriteFile(instanceMarker(), 
[]byte(strconv.Itoa(address.Port)), 0o600); err != nil {
+               fmt.Fprintln(os.Stderr, "casbin-gateway-desktop:", err)
+               listener.Close()
+               return
+       }
+
+       go serveInstance(listener)
+}
+
+func releaseInstance() {
+       _ = os.Remove(instanceMarker())
+}
+
+func serveInstance(listener net.Listener) {
+       for {
+               conn, err := listener.Accept()
+               if err != nil {
+                       return
+               }
+               go serveInstanceRequest(conn)
+       }
+}
+
+func serveInstanceRequest(conn net.Conn) {
+       defer conn.Close()
+
+       _ = conn.SetDeadline(time.Now().Add(instanceWait))
+       line, err := bufio.NewReader(conn).ReadString('\n')
+       if err != nil || strings.TrimSpace(line) != showRequest {
+               return
+       }
+
+       // Answering first keeps the launch that is waiting on this from 
outliving
+       // the window it asked for.
+       _, _ = io.WriteString(conn, showReply+"\n")
+       showWindow()
+}
diff --git a/desktop/main.go b/desktop/main.go
index a53df95..acebfcb 100644
--- a/desktop/main.go
+++ b/desktop/main.go
@@ -97,6 +97,14 @@ func main() {
                return
        }
 
+       // Clicking the shortcut of a Gateway that is already running means 
"show me
+       // the window": the launch goes to the tray that is there, rather than
+       // leaving another tray icon and another window behind every time.
+       if raiseRunningInstance() {
+               return
+       }
+
        dropOwnConsole()
+       holdInstance()
        runTray()
 }
diff --git a/desktop/tray.go b/desktop/tray.go
index 45bff82..7c96792 100644
--- a/desktop/tray.go
+++ b/desktop/tray.go
@@ -176,6 +176,7 @@ func toggleAutostart(item *systray.MenuItem) {
 
 func quit() {
        closeWindow()
+       releaseInstance()
 
        ownsServer.Lock()
        owned := ownsServer.value


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to