Skip to content
This repository has been archived by the owner on Jul 2, 2022. It is now read-only.

Commit

Permalink
v0.2.2a
Browse files Browse the repository at this point in the history
[+] Added support for non-xinerama multiple monitor setups
[*] Multiple monitor setups with different heights won't have the dead areas with artifacts anymore
[*] Fixed upload results not being otuputted to stdout when the notification is dismissed
  • Loading branch information
Francesco149 committed Oct 18, 2015
1 parent fa1a792 commit 5fd3eaf
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 51 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*~
dist
sharenix
screentest.go
screentest
11 changes: 1 addition & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,12 @@ func handleCLI() (err error) {
}

// perform upload
url, thumburl, deleteurl, err := sharenixlib.ShareNix(
_, _, _, err = sharenixlib.ShareNix(
cfg, *pmode, *psite, *psilent, *pnotification, *popen, *pclip)
if err != nil {
return
}

// display results
sharenixlib.Println(*psilent, "URL:", url)
if len(thumburl) > 0 {
sharenixlib.Println(*psilent, "Thumbnail URL:", thumburl)
}
if len(deleteurl) > 0 {
sharenixlib.Println(*psilent, "Deletion URL:", deleteurl)
}

return
}

Expand Down
7 changes: 5 additions & 2 deletions sharenixlib/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ func SniffMimeType(filePath string) (string, error) {

// first 512 bytes are used to evaluate mime type
first512 := make([]byte, 512)
file.Read(first512)
n, err := file.Read(first512)
if err != nil {
return "", err
}

return http.DetectContentType(first512), nil
return http.DetectContentType(first512[:n]), nil
}

// SendFilePostRequest prepares a multipart
Expand Down
161 changes: 125 additions & 36 deletions sharenixlib/screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,74 +18,163 @@ package sharenixlib
import (
"errors"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xinerama"
"github.com/BurntSushi/xgb/xproto"
"image"
"image/draw"
"sort"
)

// This file is heavily inspired by https://github.com/vova616/screenshot
// Full credits to vova616 for the method.

// defaultScreen returns the default screen info
func defaultScreen(con *xgb.Conn) (screen *xproto.ScreenInfo, err error) {
// get setup info
setupInfo := xproto.Setup(con)
// A ScreenRect holds information about the bounds and screen index of a monitor
type ScreenRect struct {
// Bounds of the screen (position & size)
Rect image.Rectangle
// Index of the screen in SetupInfo.Roots for xproto.
// -1 means the default screen.
ScreenIndex int
}

// ByX is a sorter for a slice of ScreenRect pointers.
type ByX []*ScreenRect

func (a ByX) Len() int { return len(a) }
func (a ByX) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByX) Less(i, j int) bool {
return a[i].Rect.Min.X < a[j].Rect.Min.X
}

// ScreenRects returns a slice of the bounds and scteen id of each monitor.
func ScreenRects(X *xgb.Conn) (rects []*ScreenRect, err error) {
// xinerama
for {
err = xinerama.Init(X)
if err != nil {
DebugPrintln(err)
break
}

var reply *xinerama.QueryScreensReply
reply, err = xinerama.QueryScreens(X).Reply()
if err != nil {
DebugPrintln(err)
break
}

if reply.Number >= 2 {
// multiple xinerama heads
DebugPrintln("Using", reply.Number, "xinerama heads")
for i, screen := range reply.ScreenInfo {
DebugPrintf("%d\tX: %d\tY: %d\tWidth: %d\tHeight: %d\n",
i, screen.XOrg, screen.YOrg, screen.Width, screen.Height)

rects = append(rects, &ScreenRect{
image.Rect(
int(screen.XOrg), int(screen.YOrg),
int(screen.XOrg)+int(screen.Width),
int(screen.YOrg)+int(screen.Height),
),
-1,
})
}
return
}

break
}

// xproto
setupInfo := xproto.Setup(X)
if setupInfo == nil {
err = errors.New("Failed to retrieve X setup info!")
err = errors.New("Failed to retrieve X setup info.")
return
}

screen = setupInfo.DefaultScreen(con)
if screen == nil {
err = errors.New("No screens detected!")
// no multiple xinerama heads,
DebugPrintln("Using", len(setupInfo.Roots), "screens")
x := 0
for i, s := range setupInfo.Roots {
DebugPrintf("%d\tX: %d\tY: 0\tWidth: %d\tHeight: %d\n",
i, x, s.WidthInPixels, s.HeightInPixels)
rects = append(rects, &ScreenRect{
image.Rect(0, 0, int(s.WidthInPixels), int(s.HeightInPixels)), i,
})
x += x + int(s.WidthInPixels)
}

return
}

// FullScreenRect returns a rectangle of the entire screen
func FullScreenRect() (rect image.Rectangle, err error) {
// connect to the X server
con, err := xgb.NewConn()
// CaptureScreen captures all screens and returns an uncompressed image
func CaptureScreen(X *xgb.Conn) (pic *image.RGBA, err error) {
rects, err := ScreenRects(X)
if err != nil {
return
}
defer con.Close()

screen, err := defaultScreen(con)
if err != nil {
return
sort.Sort(ByX(rects))

// iterate all screens and screenshot them individually. this is necessary
// even on xinerama setups because otherwise different height monitors would
// cause garbage image data in the empty areas

totalWidth := 0
totalHeight := 0
for _, r := range rects {
totalWidth += r.Rect.Dx()
if r.Rect.Dy() > totalHeight {
totalHeight = r.Rect.Dy()
}
}

rect = image.Rect(0, 0, int(screen.WidthInPixels),
int(screen.HeightInPixels))
return
}
DebugPrintln("Building", totalWidth, "x", totalHeight, "image")
pic = image.NewRGBA(image.Rect(0, 0, totalWidth, totalHeight))
draw.Draw(pic, pic.Bounds(), image.Transparent, image.ZP, draw.Src)

// CaptureScreen captures the default screen and returns an uncompressed image
func CaptureScreen() (pic *image.RGBA, err error) {
rect, err := FullScreenRect()
if err != nil {
return
x := 0
for _, r := range rects {
var screen *image.RGBA
screen, err = CaptureRect(X, r.ScreenIndex, r.Rect)
if err != nil {
return
}
finalrect := screen.Bounds().Add(image.Pt(x, 0))
draw.Draw(pic, finalrect, screen, image.ZP, draw.Src)
DebugPrintln("Drawing", finalrect)
x += r.Rect.Bounds().Dx()
}
return CaptureRect(rect)

return
}

// CaptureRect captures the given section of
// the screen and returns an uncompressed image
func CaptureRect(rect image.Rectangle) (pic *image.RGBA, err error) {
con, err := xgb.NewConn()
if err != nil {
// CaptureRect captures a section of the desired screen.
// Returns an uncompressed image.
// screenIndex = -1 gets the default screen.
func CaptureRect(X *xgb.Conn, screenIndex int, rect image.Rectangle) (
pic *image.RGBA, err error) {

// get setup info
setupInfo := xproto.Setup(X)
if setupInfo == nil {
err = errors.New("Failed to retrieve X setup info!")
return
}
defer con.Close()

screen, err := defaultScreen(con)
if err != nil {
return
var screen *xproto.ScreenInfo
if screenIndex == -1 {
screen = setupInfo.DefaultScreen(X)
if screen == nil {
err = errors.New("No default screen found")
return
}
} else {
screen = &setupInfo.Roots[screenIndex]
}

// capture screen
xImg, err := xproto.GetImage(con, xproto.ImageFormatZPixmap,
xImg, err := xproto.GetImage(X, xproto.ImageFormatZPixmap,
xproto.Drawable(screen.Root), int16(rect.Min.X), int16(rect.Min.Y),
uint16(rect.Dx()), uint16(rect.Dy()), 0xFFFFFFFF).Reply()
if err != nil {
Expand Down
23 changes: 20 additions & 3 deletions sharenixlib/sharenix.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"errors"
"flag"
"fmt"
"github.com/BurntSushi/xgb"
"github.com/conformal/gotk3/gdk"
"github.com/conformal/gotk3/glib"
"github.com/conformal/gotk3/gtk"
Expand All @@ -43,7 +44,7 @@ import (

const (
ShareNixDebug = true
ShareNixVersion = "ShareNix 0.2.1a"
ShareNixVersion = "ShareNix 0.2.2a"
)

const (
Expand Down Expand Up @@ -168,9 +169,14 @@ func UploadFullScreen(cfg *Config, sitecfg *SiteConfig, silent, notif bool) (
res *http.Response, file string, err error) {

Println(silent, "Taking screenshot...")
X, err := xgb.NewConn()
if err != nil {
return
}
defer X.Close()

// capture screen
img, err := CaptureScreen()
img, err := CaptureScreen(X)
if err != nil {
return
}
Expand Down Expand Up @@ -333,7 +339,9 @@ func UploadClipboard(cfg *Config, sitecfg *SiteConfig, silent, notif bool) (
u/url: shorten url
site: name of the target site
silent: disables all console output except errors if enabled
notification: displays a gtk notification if enabled
notification: displays a gtk notification if enabled. note that dimissing
this notification will force quit the process and the function
will never return.
open: automatically opens the uploaded file in the default browser
copyurl: stores the url in the clipboard after uploading
*/
Expand Down Expand Up @@ -440,6 +448,15 @@ func ShareNix(cfg *Config, mode, site string, silent,
err = exec.Command("xdg-open", url).Run()
}

// display results
Println(silent, "URL:", url)
if len(thumburl) > 0 {
Println(silent, "Thumbnail URL:", thumburl)
}
if len(deleteurl) > 0 {
Println(silent, "Deletion URL:", deleteurl)
}

if notification {
if err != nil {
Notifyf(notifTime, nil, "%v", err)
Expand Down
10 changes: 10 additions & 0 deletions sharenixlib/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ func DebugPrintln(a ...interface{}) (n int, err error) {
return fmt.Println(a...)
}

// DebugPrintf formats and prints the given text only
// if ShareNix is compiled with ShareNixDebug = true
func DebugPrintf(format string, a ...interface{}) (n int, err error) {
if !ShareNixDebug {
return
}
fmt.Printf("Debug: ")
return fmt.Printf(format, a...)
}

// IsImage determines if a mime type is an image or not
func IsImage(mimeType string) bool {
switch mimeType {
Expand Down

0 comments on commit 5fd3eaf

Please sign in to comment.