• Stars
    star
    3,672
  • Rank 12,043 (Top 0.3 %)
  • Language
    Go
  • License
    MIT License
  • Created about 8 years ago
  • Updated about 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Build desktop applications in Go and HTML.

GoDoc Build Status

Write desktop applications in Go, HTML, Javascript, and CSS.

Gallium is a Go library for managing windows, menus, dock icons, and desktop notifications. Each window contains a webview component, in which you code your UI in HTML. Under the hood, the webview is running Chromium.

Warning

This is an extremely early version of Gallium. Most APIs will probably change before the 1.0 release, and much of the functionality that is already implemented remains unstable.

Platforms

Only OSX is supported right now. I intend to add support for Windows and Linux soon.

Discussion

Join the #gallium channel over at the Gophers slack. (You can request an invite to the Gophers slack team here.)

Installation

Requires go >= 1.7

First install git large file storage, then install Gallium:

$ brew install git-lfs
$ git lfs install
$ go get github.com/alexflint/gallium  # will not work without git lfs!

This will fetch a 92MB framework containing a binary distribution of the Chromium content module, so it may take a few moments. This is also why git large file storage must be installed (github has a limit on file size.)

Quickstart

package main

import (
  "os"
  "runtime"

  "github.com/alexflint/gallium"
)

func main() {
  runtime.LockOSThread()         // must be the first statement in main - see below
  gallium.Loop(os.Args, onReady) // must be called from main function
}

func onReady(app *gallium.App) {
  app.OpenWindow("http://example.com/", gallium.FramedWindow)
}

To run the example as a full-fledged UI application, you need to build an app bundle:

$ go build ./example
$ go install github.com/alexflint/gallium/cmd/gallium-bundle
$ gallium-bundle example
$ open example.app

Result of the example

If you run the executable directly without building an app bundle then many UI elements, such as menus, will not work correctly.

$ go run example.go

Menus

func main() {
  runtime.LockOSThread()
  gallium.Loop(os.Args, onReady)
}

func onReady(app *gallium.App) {
  app.OpenWindow("http://example.com/", gallium.FramedWindow)
  app.SetMenu([]gallium.Menu{
    gallium.Menu{
      Title: "demo",
      Entries: []gallium.MenuEntry{
        gallium.MenuItem{
          Title:    "About",
          OnClick:  handleMenuAbout,
        },
        gallium.Separator,
        gallium.MenuItem{
          Title:    "Quit",
          Shortcut: "Cmd+q",
          OnClick:  handleMenuQuit,
        },
      },
    },
  })
}

func handleMenuAbout() {
  log.Println("about clicked")
  os.Exit(0)
}

func handleMenuQuit() {
  log.Println("quit clicked")
  os.Exit(0)
}

Menu demo

Status Bar

func main() {
  runtime.LockOSThread()
  gallium.Loop(os.Args, onReady)
}

func onReady(app *gallium.App) {
  app.OpenWindow("http://example.com/", gallium.FramedWindow)
  app.AddStatusItem(
    20,
    "statusbar",
    true,
    gallium.MenuItem{
      Title:   "Do something",
      OnClick: handleDoSomething,
    },
    gallium.MenuItem{
      Title:   "Do something else",
      OnClick: handleDoSomethingElse,
    },
  )
}

func handleDoSomething() {
  log.Println("do something")
}

func handleDoSomethingElse() {
  log.Println("do something else")
}

Statusbar demo

Desktop Notifications

Note that the OSX Notification Center determines whether or not to show any given desktop notification, so you may need to open the notification center and scroll to the bottom in order to see notifications during development.

func main() {
  runtime.LockOSThread()
  gallium.Loop(os.Args, onReady)
}

func onReady(app *gallium.App) {
  img, err := gallium.ImageFromPNG(pngBuffer)
  if err != nil {
    ...
  }

  app.Post(gallium.Notification{
    Title:    "Wow this is a notification",
    Subtitle: "The subtitle",
    Image:    img,
  })
}

Dock icons

To add a dock icon, create a directory named myapp.iconset containing the following files:

icon_16x16.png          # 16 x 16
[email protected]       # 32 x 32
icon_32x32.png          # 32 x 32
[email protected]       # 64 x 64
icon_128x128.png        # 128 x 128
[email protected]     # 256 x 256
icon_256x256.png        # 256 x 256
[email protected]     # 512 x 512
icon_512x512.png        # 512 x 512
[email protected]     # 1024 x 1024

Then build you app with

gallium-bundle myapp --icon myapp.iconset

Alternatively, if you have a .icns file:

gallium-bundle myapp --icon myapp.icns

Writing native code

You can write C or Objective-C code that interfaces directly with native windowing APIs. The following example uses the macOS native API [NSWindow setAlphaValue] to create a semi-transparent window.

package main

import (
  "log"
  "os"
  "runtime"

  "github.com/alexflint/gallium"
)

/*
#cgo CFLAGS: -x objective-c
#cgo CFLAGS: -framework Cocoa
#cgo LDFLAGS: -framework Cocoa

#include <Cocoa/Cocoa.h>
#include <dispatch/dispatch.h>

void SetAlpha(void* window, float alpha) {
  // Cocoa requires that all UI operations happen on the main thread. Since
  // gallium.Loop will have initiated the Cocoa event loop, we can can use
  // dispatch_async to run code on the main thread.
  dispatch_async(dispatch_get_main_queue(), ^{
    NSWindow* w = (NSWindow*)window;
    [w setAlphaValue:alpha];
  });
}
*/
import "C"

func onReady(ui *gallium.App) {
  window, err := ui.OpenWindow("http://example.com/", gallium.FramedWindow)
  if err != nil {
    log.Fatal(err)
  }
  C.SetAlpha(window.NativeWindow(), 0.5)
}

func main() {
  runtime.LockOSThread()
  gallium.Loop(os.Args, onReady)
}

Relationship to other projects

Electron is a well-known framework for writing desktop applications in node.js. Electron and Gallium are similar in that the core UI is developed in HTML and javascript, but with Gallium the "outer layer" of logic is written in Go. Both Electron and Gallium use Chromium under the hood, and some of the C components for Gallium were ported from Electron.

The Chromium Embedded Framework is a C framework for embedding Chromium into other applications. I investigated CEF as a basis for Gallium but decided to use libchromiumcontent instead.

cef2go is a Go wrapper for Chromium based on CEF, but so far it still requires some manual steps to use as a library.

Rationale

The goal of Gallium is to make it possible to write cross-platform desktop UI applications in Go.

Troubleshooting

"file was built for unsupported file format"

If you see the following error:

ld: warning: ignoring file go/src/github.com/alexflint/gallium/dist/Gallium.framework/Gallium, file was built for unsupported file format ( 0x76 0x65 0x72 0x73 0x69 0x6F 0x6E 0x20 0x68 0x74 0x74 0x70 0x73 0x3A 0x2F 0x2F ) which is not the architecture being linked (x86_64): go/src/github.com/alexflint/gallium/dist/Gallium.framework/Gallium

then you probably have an issue with git lfs. You can confirm that this is the problem by checking the size of the file in the error message: it should be over 1 MB, but if you see a much smaller file then this is your problem.

To fix this, try re-installing git lfs as described in the installation section above, then delete and re-install gallium.

No console output

When you run an app bundle with open Foo.app, OSX launch services discards standard output and standard error. If you need to see this output for debugging purposes, use a redirect:

gallium.RedirectStdoutStderr("output.log")

App does not start

When you run an app bundle with open Foo.app, OSX launch services will only start your app if there is not already another instance of the same application running, so if your app refuses to start then try checking the activity monitor for an already running instance.

Menus not visible

If you run the binary directly without building an app bundle then your menus will not show up, and the window will initially appear behind other applications.

UI thread issues and runtime.LockOSThread

It is very important that the first statement in your main function be runtime.LockOSThread(). The reason is that gallium calls out to various C functions in order to create and manage OSX UI elements, and many of these are required to be called from the first thread created by the process. But the Go runtime creates many threads and any one piece of Go code could end up running on any thread. The solution is runtime.LockOSThread, which tells the Go scheduler to lock the current goroutine so that it will only ever run on the current thread. Since the main function always starts off on the main thread, this wil guarantee that the later call to gallium.Loop will also be on the main thread. At this point gallium takes ownership of this thread for its main event loop and calls the OnReady callback in a separate goroutine. From this point forward it is safe to call gallium functions from any goroutine.

Shared libraries and linking issues

Gallium is based on Chromium, which it accesses via Gallium.framework. That framework in turn contains libchromiumcontent.dylib, which is a shared library containing the chromium content module and is distributed in binary form by the same folks responsible for the excellent Electron framework. When you build your Go executable, the directives in Gallium.framework instruct the linker to set up the executable to look for Gallium.framework in two places at runtime:

  1. <dir containing executable>/../Frameworks/Gallium.framework: this will resolve correctly if you choose to build and run your app as a bundle (and also means you can distribute the app bundle as a self-contained unit).
  2. $GOPATH/src/github.com/alexflint/dist/Gallium.framework: this will resolve if you choose to run your executable directly.

More Repositories

1

go-arg

Struct-based argument parsing in Go
Go
1,990
star
2

go-restructure

Match regular expressions into struct fields
Go
591
star
3

go-memdump

Very fast, very unsafe serialization for Go
Go
144
star
4

go-cloudfile

A consistent way to work with remote files.
Go
93
star
5

go-filemutex

Like sync.Mutex, but works across processes
Go
74
star
6

fscanf-speed

How fast is fscanf?
C++
58
star
7

pysfm

Python implementation of bundle adjustment
Python
55
star
8

triangulation

High quality implementations of many triangulation algorithms in pure python.
Python
33
star
9

stdroots

Standard CA roots embedded in a Go package.
Python
23
star
10

process-isolation

Elegant process isolation in pure python
Python
23
star
11

go-scalar

Go
8
star
12

manhattan-vision

Dynamic programming algorithms for reconstructing indoor Manhattan scenes
C++
8
star
13

bigquery-storage-api-example

End-to-end example in Golang for Bigquery Storage API
Go
7
star
14

go-objectgraph

Walk a runtime object graph
Go
3
star
15

jsoncat

Streaming json pretty-printer
Python
2
star
16

bufferlinks

Go
2
star
17

thesis

My PhD thesis from my computer vision research at Oxford.
TeX
2
star
18

nix-environment

The standard system tools I use everywhere to feel at home.
Python
2
star
19

image-marks

A simple graphical tool to specify manual point-to-point correspondences between two images.
Python
2
star
20

sturm-triangulation

Using sturm sequences for triangulation in inverse depth parametrization
Python
1
star
21

SublimeHoon

1
star
22

sticky-sync

Sync stickies from OSX to iOS and Android
Python
1
star
23

orangetheory

Extract data from Orange Theory emails and write as CSV
Go
1
star
24

logical-induction

Code to support the guide to logical induction for software engineers (not yet published).
Python
1
star
25

rigidbody

Rigid body transforms in 3D
Python
1
star
26

printjson

JSON pretty printer
Go
1
star
27

spline-initialization

Linear and SOCP initialization
Python
1
star
28

convex-vins-paper

Latex source for "a convex formulation of visual inertial navigation"
TeX
1
star
29

threeplot

Browser-based 3D plotting in Python
Python
1
star
30

gproj

Manage GCP projects and enabled APIs using a yaml file
Go
1
star
31

doc-publisher

Publish blog posts from Google docs to medium, lesswrong, PDF
Go
1
star
32

mutablerecord

Recordset framework for small python structures
Python
1
star
33

manifolds

Utilities for dealing with differentiable manifolds
Python
1
star
34

stl-trajectories

Show SLAM trajectories as STL models
Python
1
star
35

go-mold

Load types from Go source files
Go
1
star