diff --git a/Makefile b/Makefile index 6de5891..deb15d5 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,8 @@ launcher-windows: build-windows if exist "bin\windows\Aminal" rmdir /S /Q "bin\windows\Aminal" mkdir "bin\windows\Aminal\Versions\${VERSION}" go build -o "bin\windows\Aminal\${BINARY}.exe" -ldflags "-H windowsgui" "${GEN_SRC_DIR}\launcher" - copy ${BINARY}-windows-amd64.exe "bin\windows\Aminal\Versions\${VERSION}\${BINARY}.exe" /Y + windres -o aminal.syso aminal.rc + go build -o "bin\windows\Aminal\Versions\${VERSION}\${BINARY}.exe" -ldflags "-H windowsgui" IF "${WINDOWS_CODESIGNING_CERT_PW}"=="" ECHO Environment variable WINDOWS_CODESIGNING_CERT_PW is not defined. & exit 1 signtool sign /f windows\codesigning_certificate.pfx /p "${WINDOWS_CODESIGNING_CERT_PW}" /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp bin\windows\Aminal\${BINARY}.exe signtool sign /f windows\codesigning_certificate.pfx /p "${WINDOWS_CODESIGNING_CERT_PW}" /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /as /fd sha256 /td sha256 bin\windows\Aminal\${BINARY}.exe diff --git a/buffer/buffer.go b/buffer/buffer.go index de4c78e..f715d79 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -76,7 +76,7 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string { row := buffer.convertViewLineToRawLine((viewRow)) - uint64(buffer.terminalState.scrollLinesFromBottom) cell := buffer.GetRawCell(col, row) - if cell == nil || cell.Rune() == 0x00 { + if cell == nil || isRuneURLSelectionMarker(cell.Rune()) { return "" } @@ -84,19 +84,9 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string { return cell.hyperlink.Uri } - candidate := "" - - for i := col; i >= uint16(0); i-- { - cell := buffer.GetRawCell(i, row) - if cell == nil { - break - } - if isRuneURLSelectionMarker(cell.Rune()) { - break - } - candidate = fmt.Sprintf("%c%s", cell.Rune(), candidate) - } + candidate := string(cell.Rune()) + // First, move forward for i := col + 1; i < buffer.terminalState.viewWidth; i++ { cell := buffer.GetRawCell(i, row) if cell == nil { @@ -108,6 +98,27 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string { candidate = fmt.Sprintf("%s%c", candidate, cell.Rune()) } + // Move backwards + protocolMode := strings.Contains(candidate, "://") + for i := col - 1; i >= uint16(0); i-- { + cell := buffer.GetRawCell(i, row) + if cell == nil { + break + } + c := cell.Rune() + if isRuneURLSelectionMarker(c) { + break + } + // if we're tracking left of :// we want to break on any non-Latin character + if protocolMode && !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { + break + } + candidate = fmt.Sprintf("%c%s", c, candidate) + if !protocolMode && c == ':' { + protocolMode = strings.Contains(candidate, "://") + } + } + if candidate == "" || candidate[0] == '/' { return "" } diff --git a/glfont/font.go b/glfont/font.go index 190fd39..ac170c0 100644 --- a/glfont/font.go +++ b/glfont/font.go @@ -17,17 +17,19 @@ const DPI = 72 // A Font allows rendering of text to an OpenGL context. type Font struct { - characters map[rune]*character - vao uint32 - vbo uint32 - program uint32 - texture uint32 // Holds the glyph texture id. - color color - ttf *truetype.Font - ttfFace font.Face - scale float32 - linePadding float32 - lineHeight float32 + characters map[rune]*character + vao uint32 + vbo uint32 + program uint32 + uniformLocationResolution int32 + uniformLocationTextColor int32 + texture uint32 // Holds the glyph texture id. + color color + ttf *truetype.Font + ttfFace font.Face + scale float32 + linePadding float32 + lineHeight float32 } type color struct { @@ -46,15 +48,14 @@ func LoadFont(reader io.Reader, scale float32, windowWidth int, windowHeight int panic(err) } - // Activate corresponding render state - gl.UseProgram(program) + font, err := LoadTrueTypeFont(program, reader, scale) + if err != nil { + return nil, err + } - //set screen resolution - resUniform := gl.GetUniformLocation(program, gl.Str("resolution\x00")) - gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight)) - gl.UseProgram(0) + font.UpdateResolution(windowWidth, windowHeight) - return LoadTrueTypeFont(program, reader, scale) + return font, nil } func (f *Font) Free() { @@ -82,10 +83,8 @@ func (f *Font) SetColor(red float32, green float32, blue float32, alpha float32) func (f *Font) UpdateResolution(windowWidth int, windowHeight int) { gl.UseProgram(f.program) - resUniform := gl.GetUniformLocation(f.program, gl.Str("resolution\x00")) - gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight)) + gl.Uniform2f(f.uniformLocationResolution, float32(windowWidth), float32(windowHeight)) gl.UseProgram(0) - //f.characters = map[rune]*character{} } func (f *Font) LineHeight() float32 { @@ -112,13 +111,11 @@ func (f *Font) Print(x, y float32, text string) error { // Activate corresponding render state gl.UseProgram(f.program) //set text color - gl.Uniform4f(gl.GetUniformLocation(f.program, gl.Str("textColor\x00")), f.color.r, f.color.g, f.color.b, f.color.a) - //set screen resolution - //resUniform := gl.GetUniformLocation(f.program, gl.Str("resolution\x00")) - //gl.Uniform2f(resUniform, float32(2560), float32(1440)) + gl.Uniform4f(f.uniformLocationTextColor, f.color.r, f.color.g, f.color.b, f.color.a) gl.ActiveTexture(gl.TEXTURE0) gl.BindVertexArray(f.vao) + gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo) // Iterate through all characters in string for i := range indices { @@ -145,7 +142,7 @@ func (f *Font) Print(x, y float32, text string) error { var y2 = ypos + h //setup quad array - var vertices = []float32{ + var vertices = [...]float32{ // X, Y, Z, U, V // Front x1, y1, 0.0, 0.0, @@ -153,25 +150,24 @@ func (f *Font) Print(x, y float32, text string) error { x1, y2, 0.0, 1.0, x1, y2, 0.0, 1.0, x2, y1, 1.0, 0.0, - x2, y2, 1.0, 1.0} + x2, y2, 1.0, 1.0, + } // Render glyph texture over quad gl.BindTexture(gl.TEXTURE_2D, ch.textureID) - // Update content of VBO memory - gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo) - //BufferSubData(target Enum, offset int, data []byte) - gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(vertices)*4, gl.Ptr(vertices)) // Be sure to use glBufferSubData and not glBufferData + // Update content of VBO memory + gl.NamedBufferSubData(f.vbo, 0, len(vertices)*4, gl.Ptr(&vertices[0])) // Be sure to use glBufferSubData and not glBufferData + // Render quad gl.DrawArrays(gl.TRIANGLES, 0, 24) - gl.BindBuffer(gl.ARRAY_BUFFER, 0) // Now advance cursors for next glyph (note that advance is number of 1/64 pixels) x += float32((ch.advance >> 6)) // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels)) - } //clear opengl textures and programs + gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.BindTexture(gl.TEXTURE_2D, 0) gl.UseProgram(0) @@ -308,6 +304,7 @@ func (f *Font) GetRune(r rune) (*character, error) { gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix)) + gl.BindTexture(gl.TEXTURE_2D, 0) char.textureID = texture diff --git a/glfont/truetype.go b/glfont/truetype.go index 57af926..f713084 100644 --- a/glfont/truetype.go +++ b/glfont/truetype.go @@ -30,6 +30,9 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale float32) (*Font, error) f.scale = scale f.characters = map[rune]*character{} f.program = program //set shader program + f.uniformLocationResolution = gl.GetUniformLocation(program, gl.Str("resolution\x00")) + f.uniformLocationTextColor = gl.GetUniformLocation(program, gl.Str("textColor\x00")) + // Read the truetype font. f.ttf, err = truetype.Parse(data) if err != nil { diff --git a/gui/fonts.go b/gui/fonts.go index 911be0e..ffc663c 100644 --- a/gui/fonts.go +++ b/gui/fonts.go @@ -47,3 +47,7 @@ func (gui *GUI) loadFonts(actualWidth int, actualHeight int) error { return nil } + +func (gui *GUI) updateFontsResolution(actualWidth int, actualHeight int) { + gui.fontMap.UpdateResolution(actualWidth, actualHeight) +} diff --git a/gui/gui.go b/gui/gui.go index 3c4a337..a16a7dd 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -321,7 +321,7 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) { } gui.logger.Debugf("Updating font resolutions...") - gui.loadFonts(gui.width, gui.height) + gui.updateFontsResolution(gui.width, gui.height) gui.logger.Debugf("Setting renderer area...") gui.renderer.SetArea(0, 0, gui.width-vScrollbarWidth, gui.height) diff --git a/gui/mouse.go b/gui/mouse.go index 19703e9..daff8fc 100644 --- a/gui/mouse.go +++ b/gui/mouse.go @@ -412,13 +412,16 @@ func (gui *GUI) emitButtonEventToTerminal(tx int, ty int, button glfw.MouseButto gui.prevMotionTY = ty var packet string - if ext == terminal.MouseExtSGR { + switch ext { + case terminal.MouseExtSGR: final := 'M' if release { final = 'm' } packet = fmt.Sprintf("\x1b[<%d;%d;%d%c", b, tx, ty, final) - } else { + case terminal.MouseExtURXVT: + packet = fmt.Sprintf("\x1b[%d;%d;%dM", b+32, tx, ty) + default: packet = fmt.Sprintf("\x1b[M%c%c%c", (rune(b + 32)), (rune(tx + 32)), (rune(ty + 32))) } gui.logger.Infof("Sending mouse packet: '%v'", packet) diff --git a/terminal/ansi.go b/terminal/ansi.go index e56f697..ee181ac 100644 --- a/terminal/ansi.go +++ b/terminal/ansi.go @@ -17,6 +17,7 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{ 'P': sixelHandler, 'c': risHandler, //RIS '#': screenStateHandler, + '^': privacyMessageHandler, '(': scs0Handler, // select character set into G0 ')': scs1Handler, // select character set into G1 '*': swallowHandler(1), // character set bullshit @@ -104,3 +105,24 @@ func tabSetHandler(pty chan rune, terminal *Terminal) error { terminal.terminalState.TabSetAtCursor() return nil } + +func privacyMessageHandler(pty chan rune, terminal *Terminal) error { + // Handler should lock the terminal if there will be write operations to any data read by the renderer + // terminal.Lock() + // defer terminal.Unlock() + + isEscaped := false + for { + b := <-pty + if b == 0x18 /*CAN*/ || b == 0x1a /*SUB*/ || (b == 0x5c /*backslash*/ && isEscaped) { + break + } + if isEscaped { + isEscaped = false + } else if b == 0x1b { + isEscaped = true + continue + } + } + return nil +} diff --git a/terminal/modes.go b/terminal/modes.go index 3630e44..60a8c1f 100644 --- a/terminal/modes.go +++ b/terminal/modes.go @@ -172,6 +172,14 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { terminal.logger.Infof("Turning off SGR ext mouse mode") terminal.SetMouseExtMode(MouseExtNone) } + case "?1015": + if enabled { + terminal.logger.Infof("Turning on URXVT ext mouse mode") + terminal.SetMouseExtMode(MouseExtURXVT) + } else { + terminal.logger.Infof("Turning off URXVT ext mouse mode") + terminal.SetMouseExtMode(MouseExtNone) + } case "?1048": if enabled { terminal.ActiveBuffer().SaveCursor() diff --git a/terminal/terminal.go b/terminal/terminal.go index 2af39d6..d8a0022 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -31,6 +31,7 @@ const ( MouseExtNone MouseExtMode = iota MouseExtUTF MouseExtSGR + MouseExtURXVT ) type WindowManipulationInterface interface { diff --git a/windows/launcher/launcher.go b/windows/launcher/launcher.go index cd5cc5f..73098d8 100644 --- a/windows/launcher/launcher.go +++ b/windows/launcher/launcher.go @@ -23,10 +23,12 @@ import ( "io/ioutil" "os" "os/exec" + "os/user" "path/filepath" "sort" "strconv" "strings" + "syscall" ) type Version struct { @@ -42,8 +44,23 @@ func main() { versionsDir := filepath.Join(executableDir, "Versions") latestVersion, err := getLatestVersion(versionsDir) check(err) - target := filepath.Join(versionsDir, latestVersion, executableName) - cmd := exec.Command(target, os.Args[1:]...) + usr, err := user.Current() + check(err) + cmd := exec.Command("C:\\Windows\\System32\\cmd.exe", "/C", "start", "Aminal", "/B", executableName) + cmd.Dir = usr.HomeDir + latestVersionDir := filepath.Join(versionsDir, latestVersion) + path, pathSet := os.LookupEnv("PATH") + if pathSet { + path += ";" + latestVersionDir + } else { + path = latestVersionDir + } + cmd.Env = append(os.Environ(), "PATH="+path) + const CREATE_NO_WINDOW = 0x08000000 + cmd.SysProcAttr = &syscall.SysProcAttr{ + HideWindow: true, + CreationFlags: CREATE_NO_WINDOW, + } check(cmd.Start()) }