xbar icon indicating copy to clipboard operation
xbar copied to clipboard

Add support Windows and Linux

Open hmsjy2017 opened this issue 3 years ago • 32 comments

xbar is a good software. I want to use it in other platforms. This software is written in Go, so I think it will not be too difficult to support Windows and Linux. Thank you.

hmsjy2017 avatar Apr 05 '21 15:04 hmsjy2017

I have used this in Linux.

More precisely, Argos is a GNOME Shell extension that turns executables' standard output into panel dropdown menus. It is inspired by, and fully compatible with, the BitBar app for macOS. Argos supports many BitBar plugins without modifications, giving you access to a large library of well-tested scripts in addition to being able to write your own.

https://github.com/p-e-w/argos

xfangfang avatar Apr 05 '21 15:04 xfangfang

This software is written in Go, so I think it will not be too difficult to support Windows and Linux.

That's a wrong assumption. The programming language itself is rarely the reason for something to work or not work on certain platforms as there is hardly any popular language that doesn't work on all major platforms.

The reason why GUI applications are harder to port to other platforms is usually because of their GUI toolkits, desktop environments, and often other intricacies of the underlying operating systems. And even though xbar is built using Wails which theoretically allows you to build cross-platform GUI applications that's usually only easy for quite simple windowed applications. As soon as you enter deeper territory of the desktop environment, like docks and menu bars, it becomes more difficult.

So, while adding support for more desktop environments directly to xbar could theoretically be possible it would also add a lot of maintenance overhead. Things would have to be tested and kept up-to-date with all target platforms which are evolving as well. That would take quite some bandwidth away from developling actual features.

That is why I personally would be a big fan of a different approach.

The thing that makes xbar (and BitBar before) so great is the simple protocol between plugins and xbar's UI. This should be somehow formalized and versioned, and maybe even extracted into some kind of library or CLI tool, so that a desktop environment specific UI can be built on top of it.

Argos (as mentioned by @xfangfang) kinda already does this but only loosely and I don't think that there's a formalized or even automatable way for it to ensure compatibility.

dAnjou avatar Apr 05 '21 20:04 dAnjou

The Wails project (the tech that allows us to build the xbar UI and to control the menus) is indeed cross-platform, so when Wails v2 is released, we do expect to be able to build xbar for other operating systems. @dAnjou does raise some good points.

matryer avatar Apr 06 '21 09:04 matryer

@dAnjou does raise good points and full compatibility cross platform is a lofty goal, however not unachievable. For instance, Linux is hard to support as gnome dropped support for system trays but you can hack around it using libappindicator, etc. I'm not even sure you can get text into a windows tray icon.

leaanthony avatar Apr 06 '21 09:04 leaanthony

I think it might be possible to use OpenSwiftUI instead of SwiftUI on Windows and Linux platforms. https://github.com/Cosmo/OpenSwiftUI

hmsjy2017 avatar Apr 08 '21 16:04 hmsjy2017

@leaanthony One option is just to build a new UI that looks and works like menus for cases where traditional menu bars are unavailable. @hmsjy2017 we use Wails, not SwiftUI.

matryer avatar Apr 08 '21 16:04 matryer

I'm not even sure you can get text into a windows tray icon.

On windows It should be possible to somehow create a "toolbar" to do this. Lenovo usually has something like this to show the battery percentage preinstalled on their laptops (for some reason). Here's a screenshot i found:

image

Unfortunately, after half an hour of googling about "windows taskbar toolbars" i was no closer to finding the underlying api which allows you to do this. But I am sure there are windows development wizards out there that could help if this turns into a serious effort 😄

K1ngjulien avatar Jun 30 '21 15:06 K1ngjulien

@K1ngjulien pystray can do this on windows

https://github.com/moses-palmer/pystray/blob/master/lib/pystray/_win32.py

xfangfang avatar Jun 30 '21 15:06 xfangfang

I'd really like to see this, it would be really useful to be able to share custom xbar plugins with non-Mac users. e.g. in my team we have all 3 of these OSes represented within the team.

I figure there's 2 parts to this:

  • the xbar app to work on other OSes
  • Plugin system probably needs a defined way to label/filter plugins by OS

The app

For the most part the xbar code seems pretty cross-platform friendly. I managed to get it to compile & run on linux with just a little refactoring. But sadly, no system tray icon appeared, because ...

Wails un-readiness

Wails itself (v2) doesn't currently support system tray (or much else) on linux or windows ... They have begun ports to windows and linux, but it looks like they're doing one after the other and progress is slow. It might take years to get systray parity with the mac support ... They mention their roadmaps here: https://github.com/wailsapp/wails/issues/661

Alternatives?

I'm not sure if you're aware but https://github.com/getlantern/systray is a decent & workable system tray library in go. I've used it in Mac and Linux happily... There's one awkward limitation though, it only supports a single icon per process. Possibly because of some specific per-os limitations, I don't know the rationale.

I'm going to assume it would be really gross to try to crowbar this library it into xbar instead of wails. I mean, systray does integrate with a webview component but I just figure it'd be a big change for an app which is already working nicely for a lot of folks...

A fork/clone?

I'm tempted to build a compatible xbar clone with getlantern/systray (managing multiple processes to get multiple icons). If argos can do it in js then why not try it in another Go app ...

But really, it'd be nice not to fork/clone the app. It's preferable not to fragment an ecosystem, but I don't have the appetite to work on the C parts of Wails myself...

Any thoughts on how to go about this while playing nicely with the xbar community?


The plugin repo

It would still be good for the plugin repo to be cross-platform, even while the xbar app isn't. By that I think I'm just talking about marking and filtering plugins by OS.

xbar.os?

So, I figure it would be good to define some per-OS tagging support to the plugin system, and to have xbar's plugin browser respect those labels... xbar already defines plugin metadata fields ...

  • Maybe add a new plugin field xbar.os?
  • PluginsService.GetPlugins() should filter results by OS? (maybe send OS as a parameter for server-side filtering?)

Probably this warrants its own ticket, but I'm just airing it here first...

Cheers

laher avatar Sep 17 '21 08:09 laher

There are a number of technical reasons why this is hard, and you list some. The fact that there are libraries to do some of the work doesn't get around the fact that for a gui application, you have the problem of who owns the main event loop.

Despite these technical issues, the biggest issue with making xbar cross platform IMHO is that the concept of system trays on both Linux and Windows do not have parity with MacOS. The main difference being that they do not show text next to them. There doesn't seem to be any good solution to this other than to mimic the functionality with hacks that are a support concern, especially on Linux.

No matter what libraries you use, this disparity is still a core issue. Do you have any thoughts on a good solution?

leaanthony avatar Sep 18 '21 06:09 leaanthony

Hi, fair point that Windows can't support text in the systray. The getlantern/systray docs confirm that 'set title' takes no effect in Windows, but it shows text just fine in Linux (it uses libappindicator; I've used it with gnome shell)

... For my use case this one thing isn't a deal breaker... I'm hoping that a first line of mytitle | icon=x.png could hypothetically be used to indicate a systray icon?

... I'd be happy to use icons for the plugins I have in mind, and I think it's OK generally - assuming the possibility to mark some plugins as 'unsupported' on a per-OS basis, and then a plugin author would need some guidance in order to support multiple platforms ....

Are there any other platform-related obstacles you're aware of?

...

you have the problem of who owns the main event loop.

Sorry, I don't understand how this relates to the cross-platform thing.

laher avatar Sep 19 '21 09:09 laher

The libraries you mentioned all have a Run function that owns the main event loop.

leaanthony avatar Oct 04 '21 12:10 leaanthony

right, and xbar currently owns that loop? OK. Yeah my experiment with getlantern/systray lib is presenting each icon in a separate process and attempting some IPC (not very well as yet)... I don't know specifically why the library works this way but I'm just running with it.

You asked if I had any ideas for cross-platform support... Mainly I have just notes about obstacles and weird workarounds. Maybe some of these are helpful, maybe not ...

Icons vs text for the tray item

Icons vs text - as you say we'd need an alternative for platforms with icon-only tray items, and I haven't thought of anything amazing ...

  • Generating icons: So far I generated an icon from a font with the first letter of output. It looks OK for my personal use... I tried generating an icon from an emoji, it looked terrible. Possibly a default icon could be substituted in that case...
  • Named icons? I guess a | and a property could be added to the text, but that wouldn't be consistent with xbar's current format ... not sure how bundling such icons might work. (bundled by plugin or by xbar? maybe a plugin comment-tag could indicate where to fetch them from?)
  • I added an additional 'first' item show the text in place of the title. I also used this as the entry point for the 'xbar menu'

Refreshing the tray

Generally refreshing works fine in each platform, but with there's some challenges with the specific library I'm using ...

  • Separators and removals... in systray, it's a bit hard to remove an item, and there's no way to remove a separator. You don't get a reference to it...
  • I worked aroudn this by retaining a list of sytray menu items and replacing the text / content on refresh. Any extra items get hidden.
  • I made the 'xbar' item the top item, so that it would be easier to manage, especially with the separator... (this is also the same item which shows the 'title' on Windows). It's not amazing but it works.

Authoring plugins for cross-platform

I feel like it would be really challenging for someone to write a plugin and feel confident it was ready for cross-platform use. Little differences would trip people up IMO... But also dependency management. It'd be hard for someone to say 'well if you're on windwos then you need to install bash, oh and on mac the grep binary needs to be from gnu... etc'

  • I'm experimenting with a bundled shell and writing some plugins using that shell, such as a world clock plugin and a docker-compose plugin ... I'm trying elvish, it's kinda great to be honest, although it is a bit quirky. I'm beginning to eke out some helper functions for outputting submenus and OS-specific stuff like getting the home directory.
  • I think one of the nice things about a bundled shell with some library functions would be that you could easily make things composable. (composability is part of the magic of xbar I think, so it might take that further). Like, a plugin could represent a single docker-compose, or you could tweak it to stack multiple docker-compose instances...

OS integration ...

  • Open directory ... You can just use open / xdg-open / explorer.exe on a directory. That seems straightforward.
  • Open file. I'm not sure why xbar doesn't have an 'edit plugin script' to go with 'open plugin folder', but it seems like a nice-to-have.. I think it makes sense to drop folks into an editor ($EDITOR or $VISUAL, if set). Not sure what to do if it's not set - probably could make some platform-specific assumptions for (TextPad, Notepad), and maybe a configuration parameter... Hopefully the 'alternatives' system has an appropriate thing for editors in linux...

laher avatar Oct 04 '21 19:10 laher

Wails 2.0 was released yesterday: https://wails.io/blog/wails-v2-released/

zebroc avatar Sep 23 '22 17:09 zebroc

Wails 2.0 was released yesterday: https://wails.io/blog/wails-v2-released/

Last, but not least, a huge thank you to Mat Ryer who has provided advice and support during the development of v2. Writing xBar together using an early Alpha of v2 was helpful in shaping the direction of v2, as well as give me an understanding of some design flaws in the early releases. I'm happy to announce that as of today, we will start to port xBar to Wails v2, and it will become the flagship application for the project. Cheers Mat!

That's Awesome!

K1ngjulien avatar Sep 23 '22 18:09 K1ngjulien

@matryer do you have an estimate when the ported xBar on Wails v2 could be released?

sansar-choinyambuu avatar Nov 21 '22 16:11 sansar-choinyambuu

Is there anything for windows that exists that is at all similar to xbar?

nafg avatar Jan 24 '23 06:01 nafg

The issue is that tray icons don't support text. Would you run it if it created a bar at the top of the screen permanently? Or maybe there's a better UI?

leaanthony avatar Jan 24 '23 08:01 leaanthony

If it was auto-hide, maybe

On Tue Jan 24, 2023, 08:03 AM GMT, Lea Anthony @.***> wrote:

The issue is that tray icons don't support text. Would you run it if it created a bar at the top of the screen permanently? Or maybe there's a better UI? — Reply to this email directly, view it on GitHub https://github.com/matryer/xbar/issues/691#issuecomment-1401519378, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAYAUAII6TO5SMUS55QPLDWT6EEZANCNFSM42M6X5WA. You are receiving this because you commented.Message ID: @.***>

nafg avatar Jan 25 '23 04:01 nafg

I am using this on all desktops

https://pkg.go.dev/fyne.io/systray

works well and designed for extensibility

—-

Good example that’s like xbar ?

https://github.com/wobsoriano/systray-portable

gedw99 avatar Jan 30 '23 09:01 gedw99

Most plugins output text, and are updated multiple times a minute. Doesn't systray use bubble notifications on windows? So that would mean having a continuous stream of bubble notifications from all enabled plugins?

leaanthony avatar Jan 30 '23 19:01 leaanthony

hey

screenshot:

(https://raw.githubusercontent.com/fyne-io/systray/master/example/screenshot.png)

i don’t have windows to test on though

gedw99 avatar Jan 31 '23 19:01 gedw99

Yeah Linux isn't a problem (apart from the shambles of whether the user will need to install a shared lib or not as it isn't standard)... It's Windows that has no concept of text next to the tray icon

leaanthony avatar Jan 31 '23 19:01 leaanthony

Can someone run this on windows maybe. I would like to see what it looks like :) Sorry but just don’t have windows machine.

gedw99 avatar Jan 31 '23 19:01 gedw99

Yeah Linux isn't a problem (apart from the shambles of whether the user will need to install a shared lib or not as it isn't standard)... It's Windows that has no concept of text next to the tray icon

Fyne has installers and updaters. We can try them

line 7 and 8:

https://github.com/fynelabs/iot-blog-example/blob/main/go.mod

gedw99 avatar Jan 31 '23 19:01 gedw99

Hey. Is there any update in this case? It would be great to have xbar on Windows 🥰

mkilinskidev avatar Dec 26 '23 16:12 mkilinskidev

We would love to do that but what would it look like? Tray menus on windows do not support text so it wouldn't be a 1:1 port. Would it be a bar at the top? Same for Linux.

leaanthony avatar Dec 26 '23 20:12 leaanthony

We would love to do that but what would it look like? Tray menus on windows do not support text so it wouldn't be a 1:1 port. Would it be a bar at the top? Same for Linux.

As mentioned above, there is a systray package that allow to create an icon in the tray menu, but it doesn't support refreshing menu itself. I guess that it wouldn't be a 1:1 port because yesterday I studied the wails 2.7 package and there are a lot of differences, so probably it should be a new repository calls I don't know... wbar maybe...

mkilinskidev avatar Dec 27 '23 09:12 mkilinskidev

Now I noticed that there is a v3-alpha version available of the Wails and there is a new systray package (https://v3alpha.wails.io/whats-new/#systrays) I will try to write something using v3-alpha version for fun :)

image

mkilinskidev avatar Dec 27 '23 10:12 mkilinskidev

OK, I know that the code is not perfect and it should be refactored, but it is a prototype :) Because I used the xbar mostly for the Jira issues counter, inspired by the python Jira plugin, I've made a simple app with a dynamically generated icon tray icon and issue list.

package main

import (
	"bytes"
	_ "embed"
	"fmt"
	"image"
	"image/png"
	"log"
	"strings"
	"time"

	jira "github.com/andygrunwald/go-jira"
	"github.com/fogleman/gg"
	"github.com/wailsapp/wails/v3/pkg/application"
)

type JIRAAuth struct {
	juser  string
	jtoken string
	jql    string
	uri    string
}

func generateIcon(number int) []byte {
	const size = 16
	dc := gg.NewContext(size, size)

	dc.SetRGB(0, 0, 0)
	dc.Clear()

	dc.SetRGB(1, 1, 1)
	dc.DrawStringAnchored(fmt.Sprintf("%d", number), size/2, size/2, 0.5, 0.5)

	var img image.Image = dc.Image()
	result := new(bytes.Buffer)
	png.Encode(result, img)

	return result.Bytes()
}

func FetchJIRAIssues(JIRA JIRAAuth) *[]jira.Issue {
	tp := jira.BasicAuthTransport{
		Username: JIRA.juser,
		Password: JIRA.jtoken,
	}
	jiraClient, err := jira.NewClient(tp.Client(), JIRA.uri)

	opt := jira.SearchOptions{
		MaxResults: 20,
		Expand:     "summary,issuetype,status,assignee,project",
	}

	issues, _, err := jiraClient.Issue.Search(JIRA.jql, &opt)
	if err != nil {
		log.Fatal(err)
	}

	return &issues
}

func GenerateMenu(app *application.App, systemTray *application.SystemTray, issueTypes []string, JIRA JIRAAuth) int {
	myMenu := app.NewMenu()
	issues := FetchJIRAIssues(JIRA)
	var added bool

	for _, issueType := range issueTypes {
		added = false

		for _, issue := range *issues {
			if strings.ToUpper(issue.Fields.Status.Name) == strings.ToUpper(issueType) {
				if added == false {
					myMenu.Add(issueType).SetEnabled(false)
					added = true
				}

				label := fmt.Sprintf("[%s] %s", issue.Key, issue.Fields.Summary)
				myMenu.Add(label).
					SetTooltip(issue.Key).
					OnClick(func(ctx *application.Context) {
						app.BrowserOpenURL(fmt.Sprintf("%s/browse/%s", JIRA.uri, ctx.ClickedMenuItem().Tooltip()))
					})
			}
		}
	}

	myMenu.AddSeparator()
	myMenu.Add("Go to JIRA Issues").OnClick(func(ctx *application.Context) {
		app.BrowserOpenURL(fmt.Sprintf("%s/issues/?jql=%s", JIRA.uri, JIRA.jql))
	})
	myMenu.Add("Quit").OnClick(func(ctx *application.Context) {
		app.Quit()
	})
	systemTray.SetMenu(myMenu)

	return len(*issues)
}

func main() {
	app := application.New(application.Options{
		Name:        "JIRA Issue Counter",
		Description: "A demo of the Wails v3 API",
		Assets:      application.AlphaAssets,
		Mac: application.MacOptions{
			ActivationPolicy: application.ActivationPolicyAccessory,
		},
	})
	JIRA := JIRAAuth{
		juser:  "[email protected]",
		jtoken: "jira_ptoken",
		jql:    "assignee = currentUser() ORDER BY created DESC",
		uri:    "https://yourcompany.atlassian.net",
	}
	systemTray := app.NewSystemTray()

	var issueTypes []string
	issueTypes = append(issueTypes, "New")
	issueTypes = append(issueTypes, "In Progress")
	// issueTypes = append(issueTypes, "Oczekuje na odpowiedź klienta")
	// issueTypes = append(issueTypes, "Odpowiedź od klienta")
	// issueTypes = append(issueTypes, "Wstrzymane")

	go func() {
		for {
			cnt := GenerateMenu(app, systemTray, issueTypes, JIRA)
			systemTray.SetDarkModeIcon(generateIcon(cnt))

			time.Sleep(60 * time.Second)
		}
	}()

	err := app.Run()
	if err != nil {
		log.Fatal(err)
	}

}

image

So yes, it is possible, the wbar will have few limitations, f.ex. it is not possible to put into Windows tray a long text, but it works :)

mkilinskidev avatar Dec 29 '23 16:12 mkilinskidev