Skip to content

Commit

Permalink
Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Jérôme Schneider committed Oct 2, 2018
0 parents commit 73ece87
Show file tree
Hide file tree
Showing 16 changed files with 369 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
.DS_Store
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Bowser.app: clean Makefile src/*.go src/*.h src/*.m src/*.plist assets/*.icns assets/*.png
mkdir -p dist/Bowser.app/Contents/MacOS dist/Bowser.app/Contents/Resources
cd src && go build -i -o ../dist/Bowser.app/Contents/MacOS/bowser
cp assets/bowser.png dist/Bowser.app/Contents/MacOS
cp src/Info.plist dist/Bowser.app/Contents
cp assets/icon.icns dist/Bowser.app/Contents/Resources

.PHONY: install
install: Bowser.app
cp -Rf dist/Bowser.app /Applications

.PHONY: clean
clean:
-rm -Rf dist/Bowser.app
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# bowser - Browser switch

Bowser is a MacOS application that selects the browser to open for a URL based on rules you define.

My use case is to have 2 default browsers : one for development (Chrome), and one for surf (Safari).

## Install from binary releases

TODO

## Install from source

The installation from source requires the Apple clang environment (XCode) and Go 1.8+

```sh
$ git clone https://github.com/netgusto/bowser
$ cd bowser
$ make install
```

## Setup as default browser
Binary file added assets/bowser.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/bowser.sketch/Data
Binary file not shown.
Binary file added assets/bowser.sketch/QuickLook/Preview.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/bowser.sketch/QuickLook/Thumbnail.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions assets/bowser.sketch/metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>app</key>
<string>com.bohemiancoding.sketch</string>
<key>build</key>
<integer>5370</integer>
<key>commit</key>
<string>38aeabf36c76d40a8ed2d256f8f3b9492c3dac07</string>
<key>fonts</key>
<array/>
<key>length</key>
<integer>21163</integer>
<key>version</key>
<integer>18</integer>
</dict>
</plist>
1 change: 1 addition & 0 deletions assets/bowser.sketch/version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18
Binary file added assets/icon.icns
Binary file not shown.
55 changes: 55 additions & 0 deletions src/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>bowser</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.netgusto.bowser</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Bowser</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleVersion</key>
<string>20</string>
<key>LSUIElement</key>
<true/>
<key>LSBackgroundOnly</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>http URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>http</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>Secure http URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>https</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>Local file URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>file</string>
</array>
</dict>
</array>
</dict>
</plist>
9 changes: 9 additions & 0 deletions src/handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>

extern void ReceiveURL(char*);

@interface GoPasser : NSObject
+ (void)handleGetURLEvent:(NSAppleEventDescriptor *)event;
@end

void StartURLHandler(void);
15 changes: 15 additions & 0 deletions src/handler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "handler.h"

@implementation GoPasser
+ (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
{
ReceiveURL([[[event paramDescriptorForKeyword:keyDirectObject] stringValue] UTF8String]);
}
@end

void StartURLHandler(void) {
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:[GoPasser class]
andSelector:@selector(handleGetURLEvent:)
forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
99 changes: 99 additions & 0 deletions src/handleurl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"log"
"os"
"path/filepath"
"regexp"

gosxnotifier "github.com/deckarep/gosx-notifier"
"github.com/skratchdot/open-golang/open"
)

var devBrowser = "Google Chrome"
var surfBrowser = "Safari"

func handleURL(config Config, url string) chan interface{} {
wait := make(chan interface{})

go func() {
log.Println("Received URL")

var matchedBrowser *Browser

for _, browser := range config.Browsers {
for _, rgx := range browser.Match {
success, _ := regexp.MatchString(rgx, url)
if success {
matchedBrowser = &browser
break
}
}

if matchedBrowser != nil {
break
}
}

if matchedBrowser == nil {
// Looking for defautl browser
for _, browser := range config.Browsers {
if browser.Alias == "Default" {
matchedBrowser = &browser
break
}
}
}

if matchedBrowser == nil {
// Cannot find default browser; fall back to Safari
open.RunWith(url, "Safari")
// open.RunWith("https://github.com/netgusto/bowser", "Safari")
// open.RunWith("/tmp/hello.txt", "TextEdit")

notifyProblem(
"Cannot find a default browser - falling back to Safari\nSee https://github.com/netgusto/bowser",
"Bowser - No default browser",
)

} else {
err := open.RunWith(url, matchedBrowser.App)
if err != nil {
log.Println(err)

if err2 := open.RunWith(url, "Safari"); err2 == nil {
notifyProblem(
"URL Matched \""+matchedBrowser.Alias+"\" but failed to open \""+matchedBrowser.App+"\" - Falling back to Safari",
"Bowser - Problem with browser "+matchedBrowser.Alias,
)
} else {
notifyProblem(
"URL Matched \""+matchedBrowser.Alias+"\" but failed to open \""+matchedBrowser.App+"\" - Could not fall back to Safari",
"Bowser - Problem with browser "+matchedBrowser.Alias,
)
}
}
}

wait <- nil
}()

return wait
}

func notifyProblem(msg string, title string) {
note := gosxnotifier.NewNotification(msg)
note.Title = title
note.AppIcon = getNotificationIconPath()
note.Link = "https://github.com/netgusto/bowser#setup-default-browser"
note.Push()
}

func getNotificationIconPath() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}

return dir + "/" + "bowser.png"
}
40 changes: 40 additions & 0 deletions src/listen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

// From https://gist.github.com/nathankerr/38d8b0d45590741b57f5f79be336f07c

/*
#cgo CFLAGS: -x objective-c -Wno-incompatible-pointer-types-discards-qualifiers
#cgo LDFLAGS: -framework Foundation
#include "handler.h"
*/
import "C"

import (
"github.com/andlabs/ui"
)

//export ReceiveURL
func ReceiveURL(u *C.char) {
urlStream <- C.GoString(u)
}

func listen(config Config) chan bool {

urlStream = make(chan string, 1) // the event handler blocks!, so buffer the channel at least once to get the first message
C.StartURLHandler()

done := make(chan bool)

ui.Main(func() {
ui.QueueMain(func() { ui.Quit() })

go func() {
for url := range urlStream {
<-handleURL(config, url)
done <- true
}
}()
})

return done
}
95 changes: 95 additions & 0 deletions src/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"log"
"log/syslog"
"time"

"github.com/arkan/dotconfig"
gosxnotifier "github.com/deckarep/gosx-notifier"
"github.com/skratchdot/open-golang/open"
)

var urlStream chan string
var appname = "bowser"

func main() {

config, err := getConfig()
if err != nil {
log.Panicln(err)
}

if config.Debug {
// Configure logger to write to the syslog
logwriter, err := syslog.New(syslog.LOG_NOTICE, appname)
if err == nil {
log.SetOutput(logwriter)
}
}

select {
case <-listen(config):
if config.IsDefault {
note := gosxnotifier.NewNotification("A default configuration has been set for Bowser in ~/.config/bowser/config.yml")
note.Title = "Bowser - Please configure"
note.AppIcon = getNotificationIconPath()
note.Link = "https://github.com/netgusto/bowser#setup"
note.Push()
}
log.Println("Done")
case <-time.After(300 * time.Millisecond):
open.RunWith("https://github.com/netgusto/bowser", "Safari")
log.Println("Not received any URL after .1s; exiting")
}
}

// Config ...
type Config struct {
Debug bool `yaml:"debug"`
Browsers []Browser `yaml:"browsers,omitempty"`
IsDefault bool `yaml:"-"`
}

// Browser ...
type Browser struct {
Alias string `yaml:"alias"`
App string `yaml:"app,omitempty"`
Match []string `yaml:"match,omitempty"`
}

func getConfig() (Config, error) {
config := Config{}

if err := dotconfig.Load(appname, &config); err != nil {
if err == dotconfig.ErrConfigNotFound {
initDefaultConfig(&config)
if err := dotconfig.Save(appname, config); err != nil {
return config, err
}
}
} else if err != nil {
return config, err
}

return config, nil
}

func initDefaultConfig(config *Config) {
config.IsDefault = true
config.Debug = false
config.Browsers = []Browser{
Browser{
Alias: "Default",
App: "Safari",
},
// Browser{
// Alias: "Dev",
// App: "Google Chrome",
// Match: []string{
// "^https?://127.0.0.1",
// "^https?://localhost",
// },
// },
}
}

0 comments on commit 73ece87

Please sign in to comment.