Add support for Windows (#1374)

Initial support for Windows is implemented using the winpty translation
layer. Clipboard support for Windows is provided through the `clipboard`
crate, and font rasterization is provided by RustType.

The tty.rs file has been split into OS-specific files to separate
standard pty handling from the winpty implementation.

Several binary components are fetched via build script on windows
including libclang and winpty. These could be integrated more directly
in the future either by building those dependencies as part of the
Alacritty build process or by leveraging git lfs to store the artifacts.

Fixes #28.
This commit is contained in:
Zac Pullar-Strecker 2018-10-17 06:02:52 +13:00 committed by Joe Wilm
parent b41c6b736d
commit 15e0deae2b
36 changed files with 4323 additions and 463 deletions

4
.gitignore vendored
View File

@ -3,7 +3,9 @@ FlameGraph
# Temp files
.idea
.vscode
*.iml
*~
# vim temporary files
*.swp
@ -18,4 +20,4 @@ prime
parts
*.snap
*.pyc
alacritty_*_source.tar.bz2
alacritty_*_source.tar.bz2

View File

@ -1,11 +1,15 @@
language: rust
sudo: false
git:
depth: 1
cache: cargo
os:
- linux
- osx
- windows
rust:
- stable
@ -27,8 +31,11 @@ matrix:
env: CLIPPY=""
allow_failures:
- rust: nightly
- os: windows
script:
- if [ "$TRAVIS_OS_NAME" == "windows" ]; then choco install llvm --norestart --nosilent; fi
- if [ -n "$CLIPPY" ]; then cargo clippy --all-features --all-targets; fi
- if [ -z "$CLIPPY" ]; then cargo test; fi
- if [ -z "$CLIPPY" ]; then cargo test -p font; fi

View File

@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add support for windows
- Add terminfo capabilities advertising support for changing the window title
### Fixed

1537
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -38,12 +38,20 @@ arraydeque = "0.4"
glutin = "0.16"
env_logger = "0.5"
base64 = "0.9.0"
winit = { version = "0.15", features = ["icon_loading"] }
image = "0.19"
static_assertions = "0.2.5"
terminfo = "0.6.1"
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd"))'.dependencies]
x11-dl = "2"
[target.'cfg(windows)'.dependencies]
winpty = { path = "./winpty" }
mio-named-pipes = "0.1"
winapi = { version = "0.3.5", features = ["winuser", "synchapi", "roerrorapi", "winerror"]}
dunce = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.2"
dirs = "1.0.2"
@ -58,6 +66,12 @@ bench = []
[build-dependencies]
gl_generator = "0.9"
[target.'cfg(windows)'.build-dependencies]
embed-resource = "1.1.4"
reqwest = "0.8"
tempdir = "0.3"
zip = "0.4"
[profile.release]
lto = true
debug = 1

View File

@ -66,6 +66,10 @@ them. If something is still found to be missing, please open an issue.
apt-get install cmake libfreetype6-dev libfontconfig1-dev xclip
```
#### Windows
On windows you will need to have the `{architecture}-pc-windows-msvc` toolchain installed as well as [Clang 3.9 or greater](http://releases.llvm.org/download.html).
#### Arch Linux
On Arch Linux, you need a few extra libraries to build Alacritty. Here's a

View File

@ -1,12 +1,12 @@
Alacritty
=========
[![Build Status](https://travis-ci.org/jwilm/alacritty.svg?branch=master)](https://travis-ci.org/jwilm/alacritty)
[![Travis build Status](https://travis-ci.org/jwilm/alacritty.svg?branch=master)](https://travis-ci.org/jwilm/alacritty)
[![Appveyor build Status](https://ci.appveyor.com/api/projects/status/sjq7ya5xf3hsa59s?svg=true)](https://ci.appveyor.com/project/zacps/alacritty)
Alacritty is the fastest terminal emulator in existence. Using the GPU for
rendering enables optimizations that simply aren't possible in other emulators.
Alacritty currently supports FreeBSD, Linux, macOS, and OpenBSD. Windows
support is planned before the 1.0 release.
Alacritty currently supports macOS and Linux, and Windows.
<p align="center">
<img width="600" alt="Alacritty running vim inside tmux" src="https://cloud.githubusercontent.com/assets/4285147/21585004/2ebd0288-d06c-11e6-95d3-4a2889dbbd6f.png">
@ -24,8 +24,8 @@ The software is considered to be at an **alpha** level of readiness--there are
missing features and bugs to be fixed, but it is already used by many as a daily
driver.
Precompiled binaries will eventually be made available on supported platforms.
This is minimally blocked on a stable config format. For now, Alacritty must be
Precompiled binaries are available for Windows through [appveyor](https://ci.appveyor.com/project/jwilm/alacritty).
Precompiled binaries for other platforms are minimally blocked on a stable config format. For now, Alacritty must be
built from source.
## Further information
@ -68,14 +68,16 @@ zypper in alacritty
xbps-install alacritty
```
### Windows
Prebuilt binaries can be downloaded from the artifacts section of [appveyor](https://ci.appveyor.com/project/jwilm/alacritty).
## Configuration
Although it's possible the default configuration would work on your system,
you'll probably end up wanting to customize it anyhow. There is a default
`alacritty.yml` and `alacritty_macos.yml` at the git repository root for
Linux and macOS respectively.
Alacritty looks for the configuration file at the following paths:
`alacritty.yml`, `alacritty_macos.yml`, and `alacritty_windows.yml` at the git repository root.
Alacritty looks for the configuration file as the following paths:
1. `$XDG_CONFIG_HOME/alacritty/alacritty.yml`
2. `$XDG_CONFIG_HOME/alacritty.yml`
@ -92,7 +94,13 @@ the config file. The only exception is the `font` and `dimensions` sections
which requires Alacritty to be restarted. For further explanation of the config
file, please consult the comments in the default config file.
## Issues (known, unknown, feature requests, etc.)
### Windows
On windows the config file is located at:
`%UserProfile%\alacritty.yml`
## Issues (known, unknown, feature requests, etc)
If you run into a problem with Alacritty, please file an issue. If you've got a
feature request, feel free to ask about it. Keep in mind that Alacritty is very
@ -102,29 +110,25 @@ Just Works.
## FAQ
- **_Is it really the fastest terminal emulator?_**
**_Is it really the fastest terminal emulator?_**
In the terminals I've benchmarked against, alacritty is either faster, WAY
faster, or at least neutral. There are no benchmarks in which I've found
Alacritty to be slower.
In the terminals I've benchmarked against, alacritty is either faster, WAY
faster, or at least neutral. There are no benchmarks in which I've found
Alacritty to be slower.
- **_macOS + tmux + vim is slow! I thought this was supposed to be fast!_**
**_macOS + tmux + vim is slow! I thought this was supposed to be fast!_**
This appears to be an issue outside of terminal emulators; either macOS has an
IPC performance issue, or either tmux or vim (or both) have a bug. This same
issue can be seen in `iTerm2` and `Terminal.app`. I've found that if tmux is
running on another machine which is connected to Alacritty via SSH, this issue
disappears. Actual throughput and rendering performance are still better in
Alacritty.
This appears to be an issue outside of terminal emulators; either macOS has an
IPC performance issue, or either tmux or vim (or both) have a bug. This same
issue can be seen in `iTerm2` and `Terminal.app`. I've found that if tmux is
running on another machine which is connected to Alacritty via SSH, this issue
disappears. Actual throughput and rendering performance are still better in
Alacritty.
- **_When will Windows support be available?_**
**_My arrow keys don't work._**
When someone has time to work on it. Contributors would be welcomed :).
- **_My arrow keys don't work._**
It sounds like you deleted some key bindings from your config file. Please
reference the default config file to restore them.
It sounds like you deleted some key bindings from your config file. Please
reference the default config file to restore them.
## IRC

401
alacritty_windows.yml Normal file
View File

@ -0,0 +1,401 @@
# Configuration for Alacritty, the GPU enhanced terminal emulator
# Any items in the `env` entry below will be added as
# environment variables. Some entries may override variables
# set by alacritty it self.
env:
# TERM env customization.
#
# If this property is not set, alacritty will set it to xterm-256color.
#
# Note that some xterm terminfo databases don't declare support for italics.
# You can verify this by checking for the presence of `smso` and `sitm` in
# `infocmp xterm-256color`.
TERM: xterm-256color
window:
# Window dimensions in character columns and lines
# (changes require restart)
dimensions:
columns: 80
lines: 24
# Adds this many blank pixels of padding around the window
# Units are physical pixels; this is not DPI aware.
# (change requires restart)
padding:
x: 2
y: 2
# Window decorations
#
# Values for `decorations`:
# - full: Borders and title bar
# - none: Neither borders nor title bar
decorations: full
scrolling:
# How many lines of scrollback to keep,
# '0' will disable scrolling.
history: 10000
# Number of lines the viewport will move for every line
# scrolled when scrollback is enabled (history > 0).
multiplier: 3
# Faux Scrolling
#
# The `faux_multiplier` setting controls the number
# of lines the terminal should scroll when the alternate
# screen buffer is active. This is used to allow mouse
# scrolling for applications like `man`.
#
# To disable this completely, set `faux_multiplier` to 0.
faux_multiplier: 3
# Automatically scroll to the bottom when new text is written
# to the terminal.
auto_scroll: false
# Display tabs using this many cells (changes require restart)
tabspaces: 8
# When true, bold text is drawn using the bright variant of colors.
draw_bold_text_with_bright_colors: true
# Font configuration (changes require restart)
#
# Important font attributes like antialiasing, subpixel aa, and hinting can be
# controlled through fontconfig. Specifically, the following attributes should
# have an effect:
#
# * hintstyle
# * antialias
# * lcdfilter
# * rgba
#
# For instance, if you wish to disable subpixel antialiasing, you might set the
# rgba property to "none". If you wish to completely disable antialiasing, you
# can set antialias to false.
#
# Please see these resources for more information on how to use fontconfig
#
# * https://wiki.archlinux.org/index.php/font_configuration#Fontconfig_configuration
# * file:///usr/share/doc/fontconfig/fontconfig-user.html
font:
# The normal (roman) font face to use.
normal:
family: Consolas
# Style can be specified to pick a specific face.
# style: Regular
# The bold font face
bold:
family: Consolas
# Style can be specified to pick a specific face.
# style: Bold
# The italic font face
italic:
family: Consolas
# Style can be specified to pick a specific face.
# style: Italic
# Point size of the font
size: 11.0
# Offset is the extra space around each character. offset.y can be thought of
# as modifying the linespacing, and offset.x as modifying the letter spacing.
offset:
x: 0
y: 0
# Glyph offset determines the locations of the glyphs within their cells with
# the default being at the bottom. Increase the x offset to move the glyph to
# the right, increase the y offset to move the glyph upward.
glyph_offset:
x: 0
y: 0
# OS X only: use thin stroke font rendering. Thin strokes are suitable
# for retina displays, but for non-retina you probably want this set to
# false.
use_thin_strokes: false
# Should display the render timer
render_timer: false
# Use custom cursor colors. If true, display the cursor in the cursor.foreground
# and cursor.background colors, otherwise invert the colors of the cursor.
custom_cursor_colors: false
# Colors (Tomorrow Night Bright)
colors:
# Default colors
primary:
background: '0x000000'
foreground: '0xeaeaea'
# (Optional) Bright and Dim foreground colors
#
# The dimmed foreground color is calculated automatically if it is not present.
# If the bright foreground color is not set, or `draw_bold_text_with_bright_colors`
# is `false`, the normal foreground color will be used.
#
# dim_foreground: '0x9a9a9a'
# bright_foreground: '0xffffff'
# Colors the cursor will use if `custom_cursor_colors` is true
cursor:
text: '0x000000'
cursor: '0xffffff'
# Normal colors
normal:
black: '0x000000'
red: '0xd54e53'
green: '0xb9ca4a'
yellow: '0xe6c547'
blue: '0x7aa6da'
magenta: '0xc397d8'
cyan: '0x70c0ba'
white: '0xffffff'
# Bright colors
bright:
black: '0x666666'
red: '0xff3334'
green: '0x9ec400'
yellow: '0xe7c547'
blue: '0x7aa6da'
magenta: '0xb77ee0'
cyan: '0x54ced6'
white: '0xffffff'
# Dim colors (Optional)
dim:
black: '0x333333'
red: '0xf2777a'
green: '0x99cc99'
yellow: '0xffcc66'
blue: '0x6699cc'
magenta: '0xcc99cc'
cyan: '0x66cccc'
white: '0xdddddd'
# Visual Bell
#
# Any time the BEL code is received, Alacritty "rings" the visual bell. Once
# rung, the terminal background will be set to white and transition back to the
# default background color. You can control the rate of this transition by
# setting the `duration` property (represented in milliseconds). You can also
# configure the transition function by setting the `animation` property.
#
# Possible values for `animation`
# `Ease`
# `EaseOut`
# `EaseOutSine`
# `EaseOutQuad`
# `EaseOutCubic`
# `EaseOutQuart`
# `EaseOutQuint`
# `EaseOutExpo`
# `EaseOutCirc`
# `Linear`
#
# To completely disable the visual bell, set its duration to 0.
#
visual_bell:
animation: EaseOutExpo
duration: 0
# Background opacity
background_opacity: 1.0
# Mouse bindings
#
# Currently doesn't support modifiers. Both the `mouse` and `action` fields must
# be specified.
#
# Values for `mouse`:
# - Middle
# - Left
# - Right
# - Numeric identifier such as `5`
#
# Values for `action`:
# - Paste
# - PasteSelection
# - Copy (TODO)
mouse_bindings:
- { mouse: Middle, action: PasteSelection }
mouse:
double_click: { threshold: 300 }
triple_click: { threshold: 300 }
selection:
semantic_escape_chars: ",│`|:\"' ()[]{}<>"
hide_cursor_when_typing: false
# - Beam
cursor_style: Block
# Whether the cursor should be a hollow block on window focus loss
unfocused_hollow_cursor: true
# Live config reload (changes require restart)
live_config_reload: true
# Shell
#
# You can set shell.program to the path of your favorite shell, e.g. /bin/fish.
# Entries in shell.args are passed unmodified as arguments to the shell.
shell:
program: cmd
# args:
# - --login
# Key bindings
#
# Each binding is defined as an object with some properties. Most of the
# properties are optional. All of the alphabetical keys should have a letter for
# the `key` value such as `V`. Function keys are probably what you would expect
# as well (F1, F2, ..). The number keys above the main keyboard are encoded as
# `Key1`, `Key2`, etc. Keys on the number pad are encoded `Number1`, `Number2`,
# etc. These all match the glutin::VirtualKeyCode variants.
#
# Possible values for `mods`
# `Command`, `Super` refer to the super/command/windows key
# `Control` for the control key
# `Shift` for the Shift key
# `Alt` and `Option` refer to alt/option
#
# mods may be combined with a `|`. For example, requiring control and shift
# looks like:
#
# mods: Control|Shift
#
# The parser is currently quite sensitive to whitespace and capitalization -
# capitalization must match exactly, and piped items must not have whitespace
# around them.
#
# Either an `action`, `chars`, or `command` field must be present.
# `action` must be one of `Paste`, `PasteSelection`, `Copy`, or `Quit`.
# `chars` writes the specified string every time that binding is activated.
# These should generally be escape sequences, but they can be configured to
# send arbitrary strings of bytes.
# `command` must be a map containing a `program` string, and `args` array of
# strings. For example:
# - { ... , command: { program: "alacritty", args: ["-e", "vttest"] } }
#
# Want to add a binding (e.g. "PageUp") but are unsure what the X sequence
# (e.g. "\x1b[5~") is? Open another terminal (like xterm) without tmux,
# then run `showkey -a` to get the sequence associated to a key combination.
key_bindings:
- { key: V, mods: Control|Shift, action: Paste }
- { key: C, mods: Control|Shift, action: Copy }
- { key: Q, mods: Command, action: Quit }
- { key: W, mods: Command, action: Quit }
- { key: Insert, mods: Shift, action: PasteSelection }
- { key: Key0, mods: Control, action: ResetFontSize }
- { key: Equals, mods: Control, action: IncreaseFontSize }
- { key: Subtract, mods: Control, action: DecreaseFontSize }
- { key: Home, chars: "\x1bOH", mode: AppCursor }
- { key: Home, chars: "\x1b[H", mode: ~AppCursor }
- { key: End, chars: "\x1bOF", mode: AppCursor }
- { key: End, chars: "\x1b[F", mode: ~AppCursor }
- { key: PageUp, mods: Shift, chars: "\x1b[5;2~" }
- { key: PageUp, mods: Control, chars: "\x1b[5;5~" }
- { key: PageUp, chars: "\x1b[5~" }
- { key: PageDown, mods: Shift, chars: "\x1b[6;2~" }
- { key: PageDown, mods: Control, chars: "\x1b[6;5~" }
- { key: PageDown, chars: "\x1b[6~" }
- { key: Tab, mods: Shift, chars: "\x1b[Z" }
- { key: Back, chars: "\x7f" }
- { key: Back, mods: Alt, chars: "\x1b\x7f" }
- { key: Insert, chars: "\x1b[2~" }
- { key: Delete, chars: "\x1b[3~" }
- { key: Left, mods: Shift, chars: "\x1b[1;2D" }
- { key: Left, mods: Control, chars: "\x1b[1;5D" }
- { key: Left, mods: Alt, chars: "\x1b[1;3D" }
- { key: Left, chars: "\x1b[D", mode: ~AppCursor }
- { key: Left, chars: "\x1bOD", mode: AppCursor }
- { key: Right, mods: Shift, chars: "\x1b[1;2C" }
- { key: Right, mods: Control, chars: "\x1b[1;5C" }
- { key: Right, mods: Alt, chars: "\x1b[1;3C" }
- { key: Right, chars: "\x1b[C", mode: ~AppCursor }
- { key: Right, chars: "\x1bOC", mode: AppCursor }
- { key: Up, mods: Shift, chars: "\x1b[1;2A" }
- { key: Up, mods: Control, chars: "\x1b[1;5A" }
- { key: Up, mods: Alt, chars: "\x1b[1;3A" }
- { key: Up, chars: "\x1b[A", mode: ~AppCursor }
- { key: Up, chars: "\x1bOA", mode: AppCursor }
- { key: Down, mods: Shift, chars: "\x1b[1;2B" }
- { key: Down, mods: Control, chars: "\x1b[1;5B" }
- { key: Down, mods: Alt, chars: "\x1b[1;3B" }
- { key: Down, chars: "\x1b[B", mode: ~AppCursor }
- { key: Down, chars: "\x1bOB", mode: AppCursor }
- { key: F1, chars: "\x1bOP" }
- { key: F2, chars: "\x1bOQ" }
- { key: F3, chars: "\x1bOR" }
- { key: F4, chars: "\x1bOS" }
- { key: F5, chars: "\x1b[15~" }
- { key: F6, chars: "\x1b[17~" }
- { key: F7, chars: "\x1b[18~" }
- { key: F8, chars: "\x1b[19~" }
- { key: F9, chars: "\x1b[20~" }
- { key: F10, chars: "\x1b[21~" }
- { key: F11, chars: "\x1b[23~" }
- { key: F12, chars: "\x1b[24~" }
- { key: F1, mods: Shift, chars: "\x1b[1;2P" }
- { key: F2, mods: Shift, chars: "\x1b[1;2Q" }
- { key: F3, mods: Shift, chars: "\x1b[1;2R" }
- { key: F4, mods: Shift, chars: "\x1b[1;2S" }
- { key: F5, mods: Shift, chars: "\x1b[15;2~" }
- { key: F6, mods: Shift, chars: "\x1b[17;2~" }
- { key: F7, mods: Shift, chars: "\x1b[18;2~" }
- { key: F8, mods: Shift, chars: "\x1b[19;2~" }
- { key: F9, mods: Shift, chars: "\x1b[20;2~" }
- { key: F10, mods: Shift, chars: "\x1b[21;2~" }
- { key: F11, mods: Shift, chars: "\x1b[23;2~" }
- { key: F12, mods: Shift, chars: "\x1b[24;2~" }
- { key: F1, mods: Control, chars: "\x1b[1;5P" }
- { key: F2, mods: Control, chars: "\x1b[1;5Q" }
- { key: F3, mods: Control, chars: "\x1b[1;5R" }
- { key: F4, mods: Control, chars: "\x1b[1;5S" }
- { key: F5, mods: Control, chars: "\x1b[15;5~" }
- { key: F6, mods: Control, chars: "\x1b[17;5~" }
- { key: F7, mods: Control, chars: "\x1b[18;5~" }
- { key: F8, mods: Control, chars: "\x1b[19;5~" }
- { key: F9, mods: Control, chars: "\x1b[20;5~" }
- { key: F10, mods: Control, chars: "\x1b[21;5~" }
- { key: F11, mods: Control, chars: "\x1b[23;5~" }
- { key: F12, mods: Control, chars: "\x1b[24;5~" }
- { key: F1, mods: Alt, chars: "\x1b[1;6P" }
- { key: F2, mods: Alt, chars: "\x1b[1;6Q" }
- { key: F3, mods: Alt, chars: "\x1b[1;6R" }
- { key: F4, mods: Alt, chars: "\x1b[1;6S" }
- { key: F5, mods: Alt, chars: "\x1b[15;6~" }
- { key: F6, mods: Alt, chars: "\x1b[17;6~" }
- { key: F7, mods: Alt, chars: "\x1b[18;6~" }
- { key: F8, mods: Alt, chars: "\x1b[19;6~" }
- { key: F9, mods: Alt, chars: "\x1b[20;6~" }
- { key: F10, mods: Alt, chars: "\x1b[21;6~" }
- { key: F11, mods: Alt, chars: "\x1b[23;6~" }
- { key: F12, mods: Alt, chars: "\x1b[24;6~" }
- { key: F1, mods: Super, chars: "\x1b[1;3P" }
- { key: F2, mods: Super, chars: "\x1b[1;3Q" }
- { key: F3, mods: Super, chars: "\x1b[1;3R" }
- { key: F4, mods: Super, chars: "\x1b[1;3S" }
- { key: F5, mods: Super, chars: "\x1b[15;3~" }
- { key: F6, mods: Super, chars: "\x1b[17;3~" }
- { key: F7, mods: Super, chars: "\x1b[18;3~" }
- { key: F8, mods: Super, chars: "\x1b[19;3~" }
- { key: F9, mods: Super, chars: "\x1b[20;3~" }
- { key: F10, mods: Super, chars: "\x1b[21;3~" }
- { key: F11, mods: Super, chars: "\x1b[23;3~" }
- { key: F12, mods: Super, chars: "\x1b[24;3~" }

61
appveyor.yml Normal file
View File

@ -0,0 +1,61 @@
# Based on the "trust" template v0.1.1
# https://github.com/japaric/trust/tree/v0.1.1
environment:
global:
CRATE_NAME: alacritty
RUST_BACKTRACE: 1
APPVEYOR_SAVE_CACHE_ON_ERROR: true
matrix:
- platform: x86_64
TARGET: x86_64-pc-windows-msvc
RUST_VERSION: stable
CLIPPY: false
- platform: x86_64
TARGET: x86_64-pc-windows-msvc
RUST_VERSION: nightly
CLIPPY: true
matrix:
allow_failures:
- CLIPPY: true
shallow_clone: true
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
build_script:
# Only build once per architecture
- if [%CLIPPY%]==[false] (
cargo +%RUST_TOOLCHAIN% build --release
)
before_test:
- If [%CLIPPY%]==[true] (
rustup component add clippy-preview
)
test_script:
- if [%CLIPPY%] == [true] (
cargo %RUST_TOOLCHAIN% clippy
) else (
cargo %RUST_TOOLCHAIN% test
)
cache:
# Ideally we'd also cache the toolchain but it's too large to fit in the free cache.
# Building alacritty takes significantly longer than downloading the toolchain.
- target
artifacts:
- path: target\release\alacritty.exe
name: Alacritty
- path: target\release\winpty-agent.exe
name: Winpty agent

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, unaware</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@ -0,0 +1,9 @@
#define IDI_ICON 0x101
IDI_ICON ICON "alacritty.ico"
#define RT_MANIFEST 24
#define APP_MANIFEST 1
APP_MANIFEST RT_MANIFEST alacritty.manifest

View File

@ -11,20 +11,81 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(windows)]
extern crate embed_resource;
#[cfg(windows)]
extern crate tempdir;
#[cfg(windows)]
extern crate reqwest;
#[cfg(windows)]
extern crate zip;
#[cfg(windows)]
use tempdir::TempDir;
extern crate gl_generator;
use gl_generator::{Registry, Api, Profile, Fallbacks, GlobalGenerator};
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
use std::env;
use std::fs::File;
use std::path::Path;
#[cfg(windows)]
use std::io;
#[cfg(windows)]
use std::fs::OpenOptions;
#[cfg(windows)]
const WINPTY_PACKAGE_URL: &str = "https://www.nuget.org/api/v2/package/winpty.NET/0.4.2";
fn main() {
let dest = env::var("OUT_DIR").unwrap();
let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, [
"GL_ARB_blend_func_extended"
])
.write_bindings(GlobalGenerator, &mut file)
Registry::new(
Api::Gl,
(4, 5),
Profile::Core,
Fallbacks::All,
["GL_ARB_blend_func_extended"],
).write_bindings(GlobalGenerator, &mut file)
.unwrap();
#[cfg(windows)]
{
embed_resource::compile("assets/windows/windows.rc");
// Path is relative to target/{profile}/build/alacritty-HASH/out
let file = Path::new(&env::var("OUT_DIR").unwrap()).join("../../../winpty-agent.exe");
if !file.exists() {
aquire_winpty_agent(&file);
}
}
}
#[cfg(windows)]
fn aquire_winpty_agent(out_path: &Path) {
let tmp_dir = TempDir::new("alacritty_build").unwrap();
let mut response = reqwest::get(WINPTY_PACKAGE_URL).unwrap();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(tmp_dir.path().join("winpty_package.zip")).unwrap();
io::copy(&mut response, &mut file).unwrap();
let mut archive = zip::ZipArchive::new(file).unwrap();
let target = match env::var("TARGET").unwrap().split("-").next().unwrap() {
"x86_64" => "x86",
"i386" => "x64",
_ => panic!("architecture has no winpty binary")
};
let mut winpty_agent = archive.by_name(&format!("content/winpty/{}/winpty-agent.exe", target)).unwrap();
io::copy(&mut winpty_agent, &mut File::create(out_path).unwrap()).unwrap();
}

View File

@ -12,3 +12,6 @@ keywords = ["clipboard", "copy", "paste"]
objc = "0.2"
objc_id = "0.1"
objc-foundation = "0.1"
[target.'cfg(windows)'.dependencies]
clipboard = "0.4.2"

View File

@ -4,16 +4,20 @@
// This has to be here due to macro_use
#[cfg(target_os = "macos")]
#[macro_use] extern crate objc;
#[macro_use]
extern crate objc;
#[cfg(windows)]
extern crate clipboard;
/// An enumeration describing available clipboard buffers
pub enum Buffer {
Primary,
Selection
Selection,
}
/// Types that can get the system clipboard contents
pub trait Load : Sized {
pub trait Load: Sized {
/// Errors encountered when working with a clipboard. Each implementation is
/// allowed to define its own error type, but it must conform to std error.
type Err: ::std::error::Error + Send + Sync + 'static;
@ -45,18 +49,21 @@ pub trait Load : Sized {
///
/// Note that some platforms require the clipboard context to stay active in
/// order to load the contents from other applications.
pub trait Store : Load {
pub trait Store: Load {
/// Sets the primary clipboard contents
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String>;
where
S: Into<String>;
/// Sets the secondary clipboard contents
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String>;
where
S: Into<String>;
/// Store into the specified `buffer`.
fn store<S>(&mut self, contents: S, buffer: Buffer) -> Result<(), Self::Err>
where S: Into<String>
where
S: Into<String>,
{
match buffer {
Buffer::Selection => self.store_selection(contents),
@ -74,3 +81,8 @@ pub use x11::{Clipboard, Error};
mod macos;
#[cfg(target_os = "macos")]
pub use macos::{Clipboard, Error};
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use windows::{Clipboard, Error};

74
copypasta/src/windows.rs Normal file
View File

@ -0,0 +1,74 @@
use clipboard::ClipboardContext;
use clipboard::ClipboardProvider;
use super::{Load, Store};
pub struct Clipboard(ClipboardContext);
#[derive(Debug)]
pub enum Error {
Clipboard(Box<::std::error::Error>),
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Clipboard(..) => "error opening clipboard",
}
}
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Error::Clipboard(ref err) => err.fmt(f),
}
}
}
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
impl Load for Clipboard {
type Err = Error;
fn new() -> Result<Self, Error> {
ClipboardContext::new()
.map(Clipboard)
.map_err(Error::Clipboard)
}
fn load_primary(&self) -> Result<String, Self::Err> {
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
ctx.get_contents().map_err(Error::Clipboard)
}
fn load_selection(&self) -> Result<String, Self::Err> {
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
ctx.get_contents().map_err(Error::Clipboard)
}
}
impl Store for Clipboard {
/// Sets the primary clipboard contents
#[inline]
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
where
S: Into<String>,
{
self.0
.set_contents(contents.into())
.map_err(Error::Clipboard)
}
/// Sets the secondary clipboard contents
#[inline]
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
where
S: Into<String>,
{
self.0
.set_contents(contents.into())
.map_err(Error::Clipboard)
}
}

View File

@ -11,7 +11,7 @@ libc = "0.2"
foreign-types = "0.3"
log = "0.4"
[target.'cfg(not(target_os = "macos"))'.dependencies]
[target.'cfg(not(any(target_os = "macos", windows)))'.dependencies]
servo-fontconfig = "0.4.0"
freetype-rs = "0.19"
@ -20,3 +20,7 @@ core-foundation = "0.6"
core-text = "13"
core-graphics = "0.17"
core-foundation-sys = "0.6"
[target.'cfg(windows)'.dependencies]
font-loader = "0.6.0"
rusttype = "0.4.1"

View File

@ -139,7 +139,7 @@ impl ::Rasterize for Rasterizer {
}
/// Get metrics for font specified by FontKey
fn metrics(&self, key: FontKey) -> Result<Metrics, Error> {
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
let font = self.fonts
.get(&key)
.ok_or(Error::FontNotLoaded)?;

View File

@ -85,7 +85,7 @@ impl ::Rasterize for FreeTypeRasterizer {
})
}
fn metrics(&self, key: FontKey) -> Result<Metrics, Error> {
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
let full = self.full_metrics(key)?;
let height = (full.size_metrics.height / 64) as f64;

View File

@ -20,13 +20,11 @@
#![cfg_attr(feature = "cargo-clippy", deny(clippy, if_not_else, enum_glob_use, wrong_pub_self_convention))]
#[cfg(not(target_os = "macos"))]
#[cfg(not(any(target_os = "macos", windows)))]
extern crate fontconfig;
#[cfg(not(target_os = "macos"))]
#[cfg(not(any(target_os = "macos", windows)))]
extern crate freetype;
#[cfg(target_os = "macos")]
extern crate core_text;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
@ -34,26 +32,33 @@ extern crate core_foundation_sys;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate core_text;
#[cfg(target_os = "macos")]
extern crate euclid;
extern crate libc;
#[cfg(not(target_os = "macos"))]
#[cfg(not(any(target_os = "macos", windows)))]
#[macro_use]
extern crate foreign_types;
#[macro_use]
#[cfg_attr(not(windows), macro_use)]
extern crate log;
use std::hash::{Hash, Hasher};
use std::{fmt, cmp};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
// If target isn't macos, reexport everything from ft
#[cfg(not(target_os = "macos"))]
// If target isn't macos or windows, reexport everything from ft
#[cfg(not(any(target_os = "macos", windows)))]
pub mod ft;
#[cfg(not(target_os = "macos"))]
pub use ft::{FreeTypeRasterizer as Rasterizer, Error};
#[cfg(not(any(target_os = "macos", windows)))]
pub use ft::{Error, FreeTypeRasterizer as Rasterizer};
#[cfg(windows)]
pub mod rusttype;
#[cfg(windows)]
pub use rusttype::{Error, RustTypeRasterizer as Rasterizer};
// If target is macos, reexport everything from darwin
#[cfg(target_os = "macos")]
@ -92,14 +97,14 @@ pub enum Slant {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Weight {
Normal,
Bold
Bold,
}
/// Style of font
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Style {
Specific(String),
Description { slant: Slant, weight: Weight }
Description { slant: Slant, weight: Weight },
}
impl fmt::Display for Style {
@ -108,14 +113,15 @@ impl fmt::Display for Style {
Style::Specific(ref s) => f.write_str(&s),
Style::Description { slant, weight } => {
write!(f, "slant={:?}, weight={:?}", slant, weight)
},
}
}
}
}
impl FontDesc {
pub fn new<S>(name: S, style: Style) -> FontDesc
where S: Into<String>
where
S: Into<String>,
{
FontDesc {
name: name.into(),
@ -336,12 +342,13 @@ pub trait Rasterize {
/// Errors occurring in Rasterize methods
type Err: ::std::error::Error + Send + Sync + 'static;
/// Create a new Rasterize
/// Create a new Rasterizer
fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result<Self, Self::Err>
where Self: Sized;
where
Self: Sized;
/// Get `Metrics` for the given `FontKey`
fn metrics(&self, FontKey) -> Result<Metrics, Self::Err>;
fn metrics(&self, FontKey, Size) -> Result<Metrics, Self::Err>;
/// Load the font described by `FontDesc` and `Size`
fn load_font(&mut self, &FontDesc, Size) -> Result<FontKey, Self::Err>;

151
font/src/rusttype/mod.rs Normal file
View File

@ -0,0 +1,151 @@
extern crate font_loader;
use self::font_loader::system_fonts;
extern crate rusttype;
use self::rusttype::{point, Codepoint, FontCollection, Scale};
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight};
pub struct RustTypeRasterizer {
fonts: Vec<rusttype::Font<'static>>,
dpi_ratio: f32,
}
impl ::Rasterize for RustTypeRasterizer {
type Err = Error;
fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> {
Ok(RustTypeRasterizer {
fonts: Vec::new(),
dpi_ratio: device_pixel_ratio,
})
}
fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
let scale = Scale::uniform(size.as_f32_pts() * self.dpi_ratio * 96. / 72.);
let vmetrics = self.fonts[key.token as usize].v_metrics(scale);
let hmetrics = self.fonts[key.token as usize]
.glyph(
// If the font is monospaced all glyphs *should* have the same width
// 33 '!' is the first displaying character
Codepoint(33),
)
.ok_or(Error::MissingGlyph)?
.scaled(scale)
.h_metrics();
Ok(Metrics {
descent: vmetrics.descent,
average_advance: f64::from(hmetrics.advance_width),
line_height: f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap),
})
}
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
let fp = system_fonts::FontPropertyBuilder::new()
.family(&desc.name)
.monospace();
let fp = match desc.style {
Style::Specific(_) => unimplemented!(""),
Style::Description { slant, weight } => {
let fp = match slant {
Slant::Normal => fp,
Slant::Italic => fp.italic(),
// This style is not supported by rust-font-loader
Slant::Oblique => return Err(Error::UnsupportedStyle),
};
match weight {
Weight::Bold => fp.bold(),
Weight::Normal => fp,
}
}
};
self.fonts.push(FontCollection::from_bytes(
system_fonts::get(&fp.build())
.ok_or_else(|| Error::MissingFont(desc.clone()))?
.0,
).into_font()
.ok_or(Error::UnsupportedFont)?);
Ok(FontKey {
token: (self.fonts.len() - 1) as u16,
})
}
fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
let scaled_glyph = self.fonts[glyph_key.font_key.token as usize]
.glyph(glyph_key.c)
.ok_or(Error::MissingGlyph)?
.scaled(Scale::uniform(
glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.,
));
let glyph = scaled_glyph.positioned(point(0.0, 0.0));
// Pixel bounding box
let bb = match glyph.pixel_bounding_box() {
Some(bb) => bb,
// Bounding box calculation fails for spaces so we provide a placeholder bounding box
None => rusttype::Rect {
min: point(0, 0),
max: point(0, 0),
},
};
let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize);
glyph.draw(|_x, _y, v| {
buf.push((v * 255.0) as u8);
buf.push((v * 255.0) as u8);
buf.push((v * 255.0) as u8);
});
Ok(RasterizedGlyph {
c: glyph_key.c,
width: bb.width(),
height: bb.height(),
top: -bb.min.y,
left: bb.min.x,
buf,
})
}
}
#[derive(Debug)]
pub enum Error {
MissingFont(FontDesc),
UnsupportedFont,
UnsupportedStyle,
// NOTE: This error is different from how the FreeType code handles it
MissingGlyph,
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::MissingFont(ref _desc) => "couldn't find the requested font",
Error::UnsupportedFont => "only TrueType fonts are supported",
Error::UnsupportedStyle => "the selected style is not supported by rusttype",
Error::MissingGlyph => "the selected font did not have the requested glyph",
}
}
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Error::MissingFont(ref desc) => write!(
f,
"Couldn't find a font with {}\
\n\tPlease check the font config in your alacritty.yml.",
desc
),
Error::UnsupportedFont => write!(
f,
"Rusttype only supports TrueType fonts.\n\tPlease select a TrueType font instead."
),
Error::UnsupportedStyle => {
write!(f, "The selected font style is not supported by rusttype.")
}
Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."),
}
}
}

View File

@ -555,10 +555,12 @@ fn failure_default<'a, D, T>(deserializer: D)
}
}
#[cfg(not(target_os="macos"))]
#[cfg(not(any(windows, target_os="macos")))]
static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty.yml");
#[cfg(target_os="macos")]
static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty_macos.yml");
#[cfg(windows)]
static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty_windows.yml");
impl Default for Config {
fn default() -> Self {
@ -1444,6 +1446,7 @@ impl Config {
/// 2. $XDG_CONFIG_HOME/alacritty.yml
/// 3. $HOME/.config/alacritty/alacritty.yml
/// 4. $HOME/.alacritty.yml
#[cfg(not(windows))]
pub fn installed_config<'a>() -> Option<Cow<'a, Path>> {
// Try using XDG location by default
::xdg::BaseDirectories::with_prefix("alacritty")
@ -1472,6 +1475,19 @@ impl Config {
.map(|path| path.into())
}
#[cfg(windows)]
pub fn installed_config() -> Option<Cow<'static, Path>> {
if let Some(mut path) = ::std::env::home_dir() {
path.push("alacritty");
path.set_extension("yml");
if path.exists() {
return Some(path.into());
}
}
None
}
#[cfg(not(windows))]
pub fn write_defaults() -> io::Result<Cow<'static, Path>> {
let path = ::xdg::BaseDirectories::with_prefix("alacritty")
.map_err(|err| io::Error::new(io::ErrorKind::NotFound, ::std::error::Error::description(&err)))
@ -1480,6 +1496,15 @@ impl Config {
Ok(path.into())
}
#[cfg(windows)]
pub fn write_defaults() -> io::Result<Cow<'static, Path>> {
let path = ::std::env::home_dir()
.ok_or(io::Error::new(io::ErrorKind::NotFound, "could not find profile directory"))
.and_then(|mut p| {p.push("alacritty"); p.set_extension("yml"); Ok(p)})?;
File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?;
Ok(path.into())
}
/// Get list of colors
///
/// The ordering returned here is expected by the terminal. Colors are simply indexed in this
@ -1894,6 +1919,22 @@ impl Default for Font {
}
}
#[cfg(windows)]
impl Default for Font {
fn default() -> Font {
Font {
normal: FontDescription::new_with_family("Consolas"),
bold: FontDescription::new_with_family("Consolas"),
italic: FontDescription::new_with_family("Consolas"),
size: Size::new(11.0),
use_thin_strokes: false,
offset: Default::default(),
glyph_offset: Default::default(),
scale_with_dpi: false,
}
}
}
pub struct Monitor {
_thread: ::std::thread::JoinHandle<()>,
rx: mpsc::Receiver<Config>,
@ -1977,7 +2018,10 @@ mod tests {
#[cfg(target_os="macos")]
static ALACRITTY_YML: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_macos.yml"));
#[cfg(not(target_os="macos"))]
#[cfg(windows)]
static ALACRITTY_YML: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_windows.yml"));
#[cfg(not(any(target_os="macos", windows)))]
static ALACRITTY_YML: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml"));

View File

@ -16,7 +16,7 @@
//! GPU drawing.
use std::sync::mpsc;
use parking_lot::{MutexGuard};
use parking_lot::MutexGuard;
use Rgb;
use cli;
@ -27,7 +27,7 @@ use renderer::{self, GlyphCache, QuadRenderer};
use term::{Term, SizeInfo, RenderableCell};
use sync::FairMutex;
use window::{self, Size, Pixels, Window, SetInnerSize};
use window::{self, Pixels, SetInnerSize, Size, Window};
#[derive(Debug)]
pub enum Error {
@ -128,10 +128,7 @@ impl Display {
&self.size_info
}
pub fn new(
config: &Config,
options: &cli::Options,
) -> Result<Display, Error> {
pub fn new(config: &Config, options: &cli::Options) -> Result<Display, Error> {
// Extract some properties from config
let render_timer = config.render_timer();
@ -196,9 +193,14 @@ impl Display {
// Clear screen
let background_color = config.colors().primary.background;
renderer.with_api(config, &size_info, 0. /* visual bell intensity */, |api| {
api.clear(background_color);
});
renderer.with_api(
config,
&size_info,
0., /* visual bell intensity */
|api| {
api.clear(background_color);
},
);
Ok(Display {
window,
@ -224,9 +226,8 @@ impl Display {
info!("Initializing glyph cache");
let init_start = ::std::time::Instant::now();
let cache = renderer.with_loader(|mut api| {
GlyphCache::new(rasterizer, &font, &mut api)
})?;
let cache =
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
@ -276,7 +277,7 @@ impl Display {
&mut self,
terminal: &mut MutexGuard<Term>,
config: &Config,
items: &mut [&mut OnResize]
items: &mut [&mut OnResize],
) {
// Resize events new_size and are handled outside the poll_events
// iterator. This has the effect of coalescing multiple resize
@ -295,8 +296,7 @@ impl Display {
if new_size == None {
// Force a resize to refresh things
new_size = Some((self.size_info.width as u32,
self.size_info.height as u32));
new_size = Some((self.size_info.width as u32, self.size_info.height as u32));
}
}
@ -316,7 +316,6 @@ impl Display {
self.window.resize(w, h);
self.renderer.resize(w as i32, h as i32);
}
}
/// Draw the screen
@ -385,10 +384,15 @@ impl Display {
// Draw render timer
if self.render_timer {
let timing = format!("{:.3} usec", self.meter.average());
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| {
api.render_string(&timing[..], glyph_cache, color);
});
let color = Rgb {
r: 0xd5,
g: 0x4e,
b: 0x53,
};
self.renderer
.with_api(config, &size_info, visual_bell_intensity, |mut api| {
api.render_string(&timing[..], glyph_cache, color);
});
}
}
@ -403,7 +407,7 @@ impl Display {
/// Adjust the IME editor position according to the new location of the cursor
pub fn update_ime_position(&mut self, terminal: &Term) {
use index::{Point, Line, Column};
use index::{Column, Line, Point};
use term::SizeInfo;
let Point{line: Line(row), col: Column(col)} = terminal.cursor().point;
let SizeInfo{cell_width: cw,

View File

@ -1,20 +1,21 @@
//! The main event loop which performs I/O on the pseudoterminal
use std::borrow::Cow;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Write};
use std::io::{self, ErrorKind, Read, Write};
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
use std::marker::Send;
use mio::{self, Events, PollOpt, Ready};
#[cfg(unix)]
use mio_more::channel::{self, Receiver, Sender};
#[cfg(not(windows))]
use mio::unix::UnixReady;
use mio::unix::EventedFd;
use mio_more::channel::{self, Sender, Receiver};
use ansi;
use display;
use event;
use tty;
use term::Term;
use util::thread;
use sync::FairMutex;
@ -26,16 +27,16 @@ pub enum Msg {
Input(Cow<'static, [u8]>),
/// Indicates that the `EventLoop` should shut down, as Alacritty is shutting down
Shutdown
Shutdown,
}
/// The main event!.. loop.
///
/// Handles all the pty I/O and runs the pty parser which updates terminal
/// state.
pub struct EventLoop<Io> {
pub struct EventLoop<T: tty::EventedReadWrite> {
poll: mio::Poll,
pty: Io,
pty: T,
rx: Receiver<Msg>,
tx: Sender<Msg>,
terminal: Arc<FairMutex<Term>>,
@ -83,7 +84,7 @@ pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier {
fn notify<B>(&mut self, bytes: B)
where B: Into<Cow<'static, [u8]>>
where B: Into<Cow<'static, [u8]>>,
{
let bytes = bytes.into();
// terminal hangs if we send 0 bytes through.
@ -96,7 +97,6 @@ impl event::Notify for Notifier {
}
}
impl Default for State {
fn default() -> State {
State {
@ -163,19 +163,17 @@ impl Writing {
/// `mio::Token` for the event loop channel
const CHANNEL: mio::Token = mio::Token(0);
/// `mio::Token` for the pty file descriptor
const PTY: mio::Token = mio::Token(1);
impl<Io> EventLoop<Io>
where Io: io::Read + io::Write + Send + AsRawFd + 'static
impl<T> EventLoop<T>
where
T: tty::EventedReadWrite + Send + 'static,
{
/// Create a new event loop
pub fn new(
terminal: Arc<FairMutex<Term>>,
display: display::Notifier,
pty: Io,
pty: T,
ref_test: bool,
) -> EventLoop<Io> {
) -> EventLoop<T> {
let (tx, rx) = channel::channel();
EventLoop {
poll: mio::Poll::new().expect("create mio Poll"),
@ -203,7 +201,7 @@ impl<Io> EventLoop<Io>
match msg {
Msg::Input(input) => {
state.write_list.push_back(input);
},
}
Msg::Shutdown => {
return DrainResult::Shutdown;
}
@ -224,32 +222,22 @@ impl<Io> EventLoop<Io>
return false;
}
self.poll.reregister(
&self.rx, CHANNEL,
Ready::readable(),
PollOpt::edge() | PollOpt::oneshot()
).expect("reregister channel");
if state.needs_write() {
self.poll.reregister(
&EventedFd(&self.pty.as_raw_fd()),
PTY,
Ready::readable() | Ready::writable(),
PollOpt::edge() | PollOpt::oneshot()
).expect("reregister fd after channel recv");
}
self.poll
.reregister(&self.rx, CHANNEL, Ready::readable(), PollOpt::edge() | PollOpt::oneshot())
.unwrap();
true
}
#[inline]
fn pty_read<W>(
fn pty_read<X>(
&mut self,
state: &mut State,
buf: &mut [u8],
mut writer: Option<&mut W>
mut writer: Option<&mut X>,
) -> io::Result<()>
where W: Write
where
X: Write,
{
const MAX_READ: usize = 0x1_0000;
let mut processed = 0;
@ -259,7 +247,7 @@ impl<Io> EventLoop<Io>
let mut send_wakeup = false;
loop {
match self.pty.read(&mut buf[..]) {
match self.pty.reader().read(&mut buf[..]) {
Ok(0) => break,
Ok(got) => {
// Record bytes read; used to limit time spent in pty_read.
@ -286,23 +274,22 @@ impl<Io> EventLoop<Io>
// Run the parser
for byte in &buf[..got] {
state.parser.advance(&mut **terminal, *byte, &mut self.pty);
state
.parser
.advance(&mut **terminal, *byte, &mut self.pty.writer());
}
// Exit if we've processed enough bytes
if processed > MAX_READ {
break;
}
},
Err(err) => {
match err.kind() {
ErrorKind::Interrupted |
ErrorKind::WouldBlock => {
break;
},
_ => return Err(err),
}
}
Err(err) => match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => {
break;
}
_ => return Err(err),
},
}
}
@ -323,56 +310,53 @@ impl<Io> EventLoop<Io>
'write_many: while let Some(mut current) = state.take_current() {
'write_one: loop {
match self.pty.write(current.remaining_bytes()) {
match self.pty.writer().write(current.remaining_bytes()) {
Ok(0) => {
state.set_current(Some(current));
break 'write_many;
},
}
Ok(n) => {
current.advance(n);
if current.finished() {
state.goto_next();
break 'write_one;
}
},
}
Err(err) => {
state.set_current(Some(current));
match err.kind() {
ErrorKind::Interrupted |
ErrorKind::WouldBlock => break 'write_many,
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
_ => return Err(err),
}
}
}
}
}
Ok(())
}
pub fn spawn(
mut self,
state: Option<State>
) -> thread::JoinHandle<(EventLoop<Io>, State)> {
pub fn spawn(mut self, state: Option<State>) -> thread::JoinHandle<(Self, State)> {
thread::spawn_named("pty reader", move || {
let mut state = state.unwrap_or_else(Default::default);
let mut buf = [0u8; 0x1000];
let fd = self.pty.as_raw_fd();
let fd = EventedFd(&fd);
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
self.poll.register(&self.rx, CHANNEL, Ready::readable(), poll_opts).unwrap();
self.poll.register(&fd, PTY, Ready::readable(), poll_opts).unwrap();
let tokens = [1, 2];
self.poll
.register(&self.rx, CHANNEL, Ready::readable(), poll_opts)
.unwrap();
// Register TTY through EventedRW interface
self.pty
.register(&self.poll, &mut tokens.iter(), Ready::readable(), poll_opts).unwrap();
let mut events = Events::with_capacity(1024);
let mut pipe = if self.ref_test {
let file = File::create("./alacritty.recording")
.expect("create alacritty recording");
Some(file)
Some(File::create("./alacritty.recording").expect("create alacritty recording"))
} else {
None
};
@ -381,64 +365,68 @@ impl<Io> EventLoop<Io>
if let Err(err) = self.poll.poll(&mut events, None) {
match err.kind() {
ErrorKind::Interrupted => continue,
_ => panic!("EventLoop polling error: {:?}", err)
_ => panic!("EventLoop polling error: {:?}", err),
}
}
for event in events.iter() {
match event.token() {
CHANNEL => {
if !self.channel_event(&mut state) {
break 'event_loop;
}
CHANNEL => if !self.channel_event(&mut state) {
break 'event_loop;
},
PTY => {
let ready = event.readiness();
#[cfg(unix)] {
if UnixReady::from(ready).is_hup() {
break 'event_loop;
}
}
if ready.is_readable() {
if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
error!("Event loop exiting due to error: {} [{}:{}]",
err, file!(), line!());
break 'event_loop;
token if token == self.pty.read_token() || token == self.pty.write_token() => {
#[cfg(unix)]
{
if UnixReady::from(event.readiness()).is_hup() {
break 'event_loop;
}
}
if event.readiness().is_readable() {
if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut())
{
error!(
"Event loop exitting due to error: {} [{}:{}]",
err,
file!(),
line!()
);
break 'event_loop;
}
if ::tty::process_should_exit() {
break 'event_loop;
}
}
if ready.is_writable() {
if event.readiness().is_writable() {
if let Err(err) = self.pty_write(&mut state) {
error!("Event loop exiting due to error: {} [{}:{}]",
err, file!(), line!());
error!(
"Event loop exitting due to error: {} [{}:{}]",
err,
file!(),
line!()
);
break 'event_loop;
}
}
// Figure out pty interest
let mut interest = Ready::readable();
if state.needs_write() {
interest.insert(Ready::writable());
}
// Reregister pty
self.poll
.reregister(&fd, PTY, interest, poll_opts)
.expect("register fd after read/write");
},
}
_ => (),
}
}
// Register write interest if necessary
let mut interest = Ready::readable();
if state.needs_write() {
interest.insert(Ready::writable());
}
// Reregister with new interest
self.pty.reregister(&self.poll, interest, poll_opts).unwrap();
}
// The evented instances are not dropped here so deregister them explicitly
// TODO: Is this still necessary?
let _ = self.poll.deregister(&self.rx);
let _ = self.poll.deregister(&fd);
self.pty.deregister(&self.poll).unwrap();
(self, state)
})

View File

@ -22,6 +22,7 @@ use std::borrow::Cow;
use std::mem;
use std::process::Command;
use std::time::Instant;
#[cfg(not(windows))]
use std::os::unix::process::CommandExt;
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer};
@ -232,7 +233,9 @@ impl Action {
},
Action::Command(ref program, ref args) => {
trace!("running command: {} {:?}", program, args);
match Command::new(program)
#[cfg(not(windows))]
let spawned = Command::new(program)
.args(args)
.before_exec(|| {
// Detach forked process from Alacritty. This will cause
@ -240,7 +243,14 @@ impl Action {
unsafe { ::libc::daemon(1, 0); }
Ok(())
})
.spawn()
.spawn();
#[cfg(windows)]
let spawned = Command::new(program)
.args(args)
.spawn();
match spawned
{
Ok(child) => {
debug!("spawned new proc with pid: {}", child.id());

View File

@ -23,9 +23,23 @@
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate static_assertions;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd"))]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly",
target_os = "openbsd"))]
extern crate x11_dl;
#[cfg(windows)]
extern crate mio_named_pipes;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate winpty;
#[cfg(windows)]
extern crate dunce;
#[cfg(windows)]
extern crate winit;
#[cfg(windows)]
extern crate image;
#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
@ -33,8 +47,8 @@ extern crate objc;
extern crate arraydeque;
extern crate cgmath;
extern crate copypasta;
extern crate errno;
extern crate env_logger;
extern crate errno;
extern crate fnv;
extern crate font;
extern crate glutin;

View File

@ -17,6 +17,12 @@
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
// With the default subsystem, 'console', windows creates an additional console
// window for the program.
// This is silently ignored on non-windows systems.
// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[macro_use]
extern crate alacritty;
@ -29,6 +35,8 @@ use std::error::Error;
use std::sync::Arc;
#[cfg(target_os = "macos")]
use std::env;
#[cfg(not(windows))]
use std::os::unix::io::AsRawFd;
use alacritty::cli;
use alacritty::config::{self, Config};
@ -39,7 +47,7 @@ use alacritty::event_loop::{self, EventLoop, Msg};
use alacritty::locale;
use alacritty::logging;
use alacritty::sync::FairMutex;
use alacritty::term::{Term};
use alacritty::term::Term;
use alacritty::tty::{self, process_should_exit};
use alacritty::util::fmt::Red;
@ -67,7 +75,8 @@ fn main() {
///
/// If a configuration file is given as a command line argument we don't
/// generate a default file. If an empty configuration file is given, i.e.
/// /dev/null, we load the compiled-in defaults.
/// /dev/null, we load the compiled-in defaults.)
#[cfg(not(windows))]
fn load_config(options: &cli::Options) -> Config {
let config_path = options.config_path()
.or_else(Config::installed_config)
@ -81,6 +90,27 @@ fn load_config(options: &cli::Options) -> Config {
Config::default()
})
}
#[cfg(windows)]
fn load_config(options: &cli::Options) -> Config {
let config_path = options
.config_path()
.or_else(|| Config::installed_config())
.unwrap_or_else(|| {
Config::write_defaults()
.unwrap_or_else(|err| die!("Write defaults config failure: {}", err))
});
Config::load_from(&*config_path).unwrap_or_else(|err| match err {
config::Error::NotFound => {
die!("Config file not found after writing: {}", config_path.display());
}
config::Error::Empty => {
eprintln!("Empty config; Loading defaults");
Config::default()
}
_ => die!("{}", err),
})
}
/// Run Alacritty
///
@ -122,7 +152,17 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
// The pty forks a process to run the shell on the slave side of the
// pseudoterminal. A file descriptor for the master side is retained for
// reading/writing to the shell.
let mut pty = tty::new(&config, options, &display.size(), window_id);
let pty = tty::new(&config, options, &display.size(), window_id);
// Get a reference to something that we can resize
//
// This exists because rust doesn't know the interface is thread-safe
// and we need to be able to resize the PTY from the main thread while the IO
// thread owns the EventedRW object.
#[cfg(windows)]
let resize_handle = unsafe { &mut *pty.winpty.get() };
#[cfg(not(windows))]
let mut resize_handle = pty.fd.as_raw_fd();
// Create the pseudoterminal I/O loop
//
@ -133,7 +173,7 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
let event_loop = EventLoop::new(
Arc::clone(&terminal),
display.notifier(),
pty.reader(),
pty,
options.ref_test,
);
@ -168,7 +208,9 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
};
// Kick off the I/O thread
let io_thread = event_loop.spawn(None);
let _io_thread = event_loop.spawn(None);
info!("Initialisation complete");
// Main display loop
loop {
@ -195,7 +237,11 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
//
// The second argument is a list of types that want to be notified
// of display size changes.
display.handle_resize(&mut terminal_lock, &config, &mut [&mut pty, &mut processor]);
#[cfg(windows)]
display.handle_resize(&mut terminal_lock, &config, &mut [resize_handle, &mut processor]);
#[cfg(not(windows))]
display.handle_resize(&mut terminal_lock, &config, &mut [&mut resize_handle, &mut processor]);
drop(terminal_lock);
// Draw the current state of the terminal
@ -208,13 +254,12 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
}
}
loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to event loop");
loop_tx
.send(Msg::Shutdown)
.expect("Error sending shutdown to event loop");
// FIXME patch notify library to have a shutdown method
// config_reloader.join().ok();
// Wait for the I/O thread thread to finish
let _ = io_thread.join();
Ok(())
}

View File

@ -16,22 +16,22 @@ use std::fs::File;
use std::hash::BuildHasherDefault;
use std::io::{self, Read};
use std::mem::size_of;
use std::path::{PathBuf};
use std::path::PathBuf;
use std::ptr;
use std::sync::mpsc;
use std::time::Duration;
use cgmath;
use fnv::FnvHasher;
use font::{self, Rasterizer, Rasterize, RasterizedGlyph, FontDesc, GlyphKey, FontKey};
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
use gl::types::*;
use gl;
use index::{Line, Column, RangeInclusive};
use notify::{Watcher, watcher, RecursiveMode, DebouncedEvent};
use index::{Column, Line, RangeInclusive};
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use config::{self, Config, Delta};
use term::{self, cell, RenderableCell};
use window::{Size, Pixels};
use window::{Pixels, Size};
use Rgb;
@ -40,12 +40,10 @@ static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/
static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
// Shader source which is used when live-shader-reload feature is disable
static TEXT_SHADER_F: &'static str = include_str!(
concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl")
);
static TEXT_SHADER_V: &'static str = include_str!(
concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl")
);
static TEXT_SHADER_F: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl"));
static TEXT_SHADER_V: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl"));
/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory
pub trait LoadGlyph {
@ -97,7 +95,6 @@ impl From<ShaderCreationError> for Error {
}
}
/// Text drawing program
///
/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
@ -127,7 +124,6 @@ pub struct ShaderProgram {
padding_y: u8,
}
#[derive(Debug, Clone)]
pub struct Glyph {
tex_id: GLuint,
@ -174,9 +170,10 @@ impl GlyphCache {
pub fn new<L>(
mut rasterizer: Rasterizer,
font: &config::Font,
loader: &mut L
loader: &mut L,
) -> Result<GlyphCache, font::Error>
where L: LoadGlyph
where
L: LoadGlyph,
{
let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?;
@ -184,7 +181,8 @@ impl GlyphCache {
// The glyph requested here ('m' at the time of writing) has no special
// meaning.
rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
let metrics = rasterizer.metrics(regular)?;
let metrics = rasterizer.metrics(regular, font.size())?;
let mut cache = GlyphCache {
cache: HashMap::default(),
@ -204,11 +202,7 @@ impl GlyphCache {
Ok(cache)
}
fn load_glyphs_for_font<L: LoadGlyph>(
&mut self,
font: FontKey,
loader: &mut L,
) {
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
let size = self.font_size;
for i in RangeInclusive::new(32u8, 128u8) {
self.get(GlyphKey {
@ -222,22 +216,23 @@ impl GlyphCache {
/// Computes font keys for (Regular, Bold, Italic)
fn compute_font_keys(
font: &config::Font,
rasterizer: &mut Rasterizer
rasterizer: &mut Rasterizer,
) -> Result<(FontKey, FontKey, FontKey), font::Error> {
let size = font.size();
// Load regular font
let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal);
let regular = rasterizer
.load_font(&regular_desc, size)?;
let regular = rasterizer.load_font(&regular_desc, size)?;
// helper to load a description if it is not the regular_desc
let mut load_or_regular = |desc:FontDesc| {
let mut load_or_regular = |desc: FontDesc| {
if desc == regular_desc {
regular
} else {
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
rasterizer
.load_font(&desc, size)
.unwrap_or_else(|_| regular)
}
};
@ -269,7 +264,7 @@ impl GlyphCache {
pub fn font_metrics(&self) -> font::Metrics {
self.rasterizer
.metrics(self.font_key)
.metrics(self.font_key, self.font_size)
.expect("metrics load since font is loaded at glyph cache creation")
}
@ -290,7 +285,7 @@ impl GlyphCache {
rasterized.top -= metrics.descent as i32;
loader.load_glyph(&rasterized)
})
})
}
pub fn update_font_size<L: LoadGlyph>(
&mut self,
@ -306,8 +301,9 @@ impl GlyphCache {
let font = font.to_owned().with_size(size);
info!("Font size changed: {:?}", font.size);
let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?;
self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
let metrics = self.rasterizer.metrics(regular)?;
let metrics = self.rasterizer.metrics(regular, size)?;
self.font_size = font.size;
self.font_key = regular;
@ -405,11 +401,7 @@ impl Batch {
}
}
pub fn add_item(
&mut self,
cell: &RenderableCell,
glyph: &Glyph,
) {
pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
if self.is_empty() {
self.tex = glyph.tex_id;
}
@ -509,69 +501,99 @@ impl QuadRenderer {
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::VertexAttribPointer(0, 2,
gl::FLOAT, gl::FALSE,
size_of::<PackedVertex>() as i32,
ptr::null());
gl::VertexAttribPointer(
0,
2,
gl::FLOAT,
gl::FALSE,
size_of::<PackedVertex>() as i32,
ptr::null(),
);
gl::EnableVertexAttribArray(0);
gl::BufferData(gl::ARRAY_BUFFER,
(size_of::<PackedVertex>() * vertices.len()) as GLsizeiptr,
vertices.as_ptr() as *const _,
gl::STATIC_DRAW);
gl::BufferData(
gl::ARRAY_BUFFER,
(size_of::<PackedVertex>() * vertices.len()) as GLsizeiptr,
vertices.as_ptr() as *const _,
gl::STATIC_DRAW,
);
// ---------------------
// Set up element buffer
// ---------------------
let indices: [u32; 6] = [0, 1, 3,
1, 2, 3];
let indices: [u32; 6] = [0, 1, 3, 1, 2, 3];
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
(6 * size_of::<u32>()) as isize,
indices.as_ptr() as *const _,
gl::STATIC_DRAW);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(6 * size_of::<u32>()) as isize,
indices.as_ptr() as *const _,
gl::STATIC_DRAW,
);
// ----------------------------
// Setup vertex instance buffer
// ----------------------------
gl::BindBuffer(gl::ARRAY_BUFFER, vbo_instance);
gl::BufferData(gl::ARRAY_BUFFER,
(BATCH_MAX * size_of::<InstanceData>()) as isize,
ptr::null(), gl::STREAM_DRAW);
gl::BufferData(
gl::ARRAY_BUFFER,
(BATCH_MAX * size_of::<InstanceData>()) as isize,
ptr::null(),
gl::STREAM_DRAW,
);
// coords
gl::VertexAttribPointer(1, 2,
gl::FLOAT, gl::FALSE,
size_of::<InstanceData>() as i32,
ptr::null());
gl::VertexAttribPointer(
1,
2,
gl::FLOAT,
gl::FALSE,
size_of::<InstanceData>() as i32,
ptr::null(),
);
gl::EnableVertexAttribArray(1);
gl::VertexAttribDivisor(1, 1);
// glyphoffset
gl::VertexAttribPointer(2, 4,
gl::FLOAT, gl::FALSE,
size_of::<InstanceData>() as i32,
(2 * size_of::<f32>()) as *const _);
gl::VertexAttribPointer(
2,
4,
gl::FLOAT,
gl::FALSE,
size_of::<InstanceData>() as i32,
(2 * size_of::<f32>()) as *const _,
);
gl::EnableVertexAttribArray(2);
gl::VertexAttribDivisor(2, 1);
// uv
gl::VertexAttribPointer(3, 4,
gl::FLOAT, gl::FALSE,
size_of::<InstanceData>() as i32,
(6 * size_of::<f32>()) as *const _);
gl::VertexAttribPointer(
3,
4,
gl::FLOAT,
gl::FALSE,
size_of::<InstanceData>() as i32,
(6 * size_of::<f32>()) as *const _,
);
gl::EnableVertexAttribArray(3);
gl::VertexAttribDivisor(3, 1);
// color
gl::VertexAttribPointer(4, 3,
gl::FLOAT, gl::FALSE,
size_of::<InstanceData>() as i32,
(10 * size_of::<f32>()) as *const _);
gl::VertexAttribPointer(
4,
3,
gl::FLOAT,
gl::FALSE,
size_of::<InstanceData>() as i32,
(10 * size_of::<f32>()) as *const _,
);
gl::EnableVertexAttribArray(4);
gl::VertexAttribDivisor(4, 1);
// color
gl::VertexAttribPointer(5, 4,
gl::FLOAT, gl::FALSE,
size_of::<InstanceData>() as i32,
(13 * size_of::<f32>()) as *const _);
gl::VertexAttribPointer(
5,
4,
gl::FLOAT,
gl::FALSE,
size_of::<InstanceData>() as i32,
(13 * size_of::<f32>()) as *const _,
);
gl::EnableVertexAttribArray(5);
gl::VertexAttribDivisor(5, 1);
@ -585,18 +607,23 @@ impl QuadRenderer {
::std::thread::spawn(move || {
let (tx, rx) = ::std::sync::mpsc::channel();
// The Duration argument is a debouncing period.
let mut watcher = watcher(tx, Duration::from_millis(10)).expect("create file watcher");
watcher.watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
.expect("watch fragment shader");
watcher.watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
.expect("watch vertex shader");
let mut watcher =
watcher(tx, Duration::from_millis(10)).expect("create file watcher");
watcher
.watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
.expect("watch fragment shader");
watcher
.watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
.expect("watch vertex shader");
loop {
let event = rx.recv().expect("watcher event");
match event {
DebouncedEvent::Rename(_, _) => continue,
DebouncedEvent::Create(_) | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => {
DebouncedEvent::Create(_)
| DebouncedEvent::Write(_)
| DebouncedEvent::Chmod(_) => {
msg_tx.send(Msg::ShaderReload).expect("msg send ok");
}
_ => {}
@ -629,17 +656,21 @@ impl QuadRenderer {
config: &Config,
props: &term::SizeInfo,
visual_bell_intensity: f64,
func: F
func: F,
) -> T
where F: FnOnce(RenderApi) -> T
where
F: FnOnce(RenderApi) -> T,
{
while let Ok(msg) = self.rx.try_recv() {
match msg {
Msg::ShaderReload => {
self.reload_shaders(config, Size {
width: Pixels(props.width as u32),
height: Pixels(props.height as u32)
});
self.reload_shaders(
config,
Size {
width: Pixels(props.width as u32),
height: Pixels(props.height as u32),
},
);
}
}
}
@ -677,7 +708,8 @@ impl QuadRenderer {
}
pub fn with_loader<F, T>(&mut self, func: F) -> T
where F: FnOnce(LoaderApi) -> T
where
F: FnOnce(LoaderApi) -> T,
{
unsafe {
gl::ActiveTexture(gl::TEXTURE0);
@ -696,12 +728,12 @@ impl QuadRenderer {
Ok(program) => {
warn!(" ... OK");
program
},
}
Err(err) => {
match err {
ShaderCreationError::Io(err) => {
error!("Error reading shader file: {}", err);
},
}
ShaderCreationError::Compile(path, log) => {
error!("Error compiling shader at {:?}\n{}", path, log);
}
@ -724,7 +756,12 @@ impl QuadRenderer {
// viewport
unsafe {
gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
gl::Viewport(
padding_x,
padding_y,
width - 2 * padding_x,
height - 2 * padding_y,
);
}
// update projection
@ -750,8 +787,12 @@ impl<'a> RenderApi<'a> {
fn render_batch(&mut self) {
unsafe {
gl::BufferSubData(gl::ARRAY_BUFFER, 0, self.batch.size() as isize,
self.batch.instances.as_ptr() as *const _);
gl::BufferSubData(
gl::ARRAY_BUFFER,
0,
self.batch.size() as isize,
self.batch.instances.as_ptr() as *const _,
);
}
// Bind texture if necessary
@ -764,29 +805,33 @@ impl<'a> RenderApi<'a> {
unsafe {
self.program.set_background_pass(true);
gl::DrawElementsInstanced(gl::TRIANGLES,
6, gl::UNSIGNED_INT, ptr::null(),
self.batch.len() as GLsizei);
gl::DrawElementsInstanced(
gl::TRIANGLES,
6,
gl::UNSIGNED_INT,
ptr::null(),
self.batch.len() as GLsizei,
);
self.program.set_background_pass(false);
gl::DrawElementsInstanced(gl::TRIANGLES,
6, gl::UNSIGNED_INT, ptr::null(),
self.batch.len() as GLsizei);
gl::DrawElementsInstanced(
gl::TRIANGLES,
6,
gl::UNSIGNED_INT,
ptr::null(),
self.batch.len() as GLsizei,
);
}
self.batch.clear();
}
/// Render a string in a predefined location. Used for printing render time for profiling and
/// optimization.
pub fn render_string(
&mut self,
string: &str,
glyph_cache: &mut GlyphCache,
color: Rgb,
) {
pub fn render_string(&mut self, string: &str, glyph_cache: &mut GlyphCache, color: Rgb) {
let line = Line(23);
let col = Column(0);
let cells = string.chars()
let cells = string
.chars()
.enumerate()
.map(|(i, c)| RenderableCell {
line,
@ -795,7 +840,7 @@ impl<'a> RenderApi<'a> {
bg: color,
fg: Rgb { r: 0, g: 0, b: 0 },
flags: cell::Flags::empty(),
bg_alpha: 1.0
bg_alpha: 1.0,
})
.collect::<Vec<_>>();
@ -838,7 +883,7 @@ impl<'a> RenderApi<'a> {
let mut glyph_key = GlyphKey {
font_key,
size: glyph_cache.font_size,
c: cell.c
c: cell.c,
};
// Don't render text of HIDDEN cells
@ -859,7 +904,7 @@ impl<'a> RenderApi<'a> {
let glyph_key = GlyphKey {
font_key,
size: glyph_cache.font_size,
c: '_'
c: '_',
};
let underscore = glyph_cache.get(glyph_key, self);
@ -959,28 +1004,22 @@ impl ShaderProgram {
pub fn new(
config: &Config,
size: Size<Pixels<u32>>
size: Size<Pixels<u32>>,
) -> Result<ShaderProgram, ShaderCreationError> {
let vertex_source = if cfg!(feature = "live-shader-reload") {
None
} else {
Some(TEXT_SHADER_V)
};
let vertex_shader = ShaderProgram::create_shader(
TEXT_SHADER_V_PATH,
gl::VERTEX_SHADER,
vertex_source
)?;
let vertex_shader =
ShaderProgram::create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_source)?;
let frag_source = if cfg!(feature = "live-shader-reload") {
None
} else {
Some(TEXT_SHADER_F)
};
let fragment_shader = ShaderProgram::create_shader(
TEXT_SHADER_F_PATH,
gl::FRAGMENT_SHADER,
frag_source
)?;
let fragment_shader =
ShaderProgram::create_shader(TEXT_SHADER_F_PATH, gl::FRAGMENT_SHADER, frag_source)?;
let program = ShaderProgram::create_program(vertex_shader, fragment_shader)?;
unsafe {
@ -1060,10 +1099,13 @@ impl ShaderProgram {
info!("width: {}, height: {}", width, height);
unsafe {
gl::UniformMatrix4fv(self.u_projection,
1, gl::FALSE, projection.as_ptr() as *const _);
gl::UniformMatrix4fv(
self.u_projection,
1,
gl::FALSE,
projection.as_ptr() as *const _,
);
}
}
fn set_term_uniforms(&self, props: &term::SizeInfo) {
@ -1080,11 +1122,7 @@ impl ShaderProgram {
}
fn set_background_pass(&self, background_pass: bool) {
let value = if background_pass {
1
} else {
0
};
let value = if background_pass { 1 } else { 0 };
unsafe {
gl::Uniform1i(self.u_background, value);
@ -1109,11 +1147,10 @@ impl ShaderProgram {
}
}
fn create_shader(
path: &str,
kind: GLenum,
source: Option<&'static str>
source: Option<&'static str>,
) -> Result<GLuint, ShaderCreationError> {
let from_disk;
let source = if let Some(src) = source {
@ -1144,7 +1181,9 @@ impl ShaderProgram {
let log = get_shader_info_log(shader);
// Cleanup
unsafe { gl::DeleteShader(shader); }
unsafe {
gl::DeleteShader(shader);
}
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
}
@ -1170,7 +1209,12 @@ fn get_program_info_log(program: GLuint) -> String {
let mut actual_length: GLint = 0;
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
unsafe {
gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
gl::GetProgramInfoLog(
program,
max_length,
&mut actual_length,
buf.as_mut_ptr() as *mut _,
);
}
// Build a string
@ -1193,7 +1237,12 @@ fn get_shader_info_log(shader: GLuint) -> String {
let mut actual_length: GLint = 0;
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
unsafe {
gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
gl::GetShaderInfoLog(
shader,
max_length,
&mut actual_length,
buf.as_mut_ptr() as *mut _,
);
}
// Build a string
@ -1248,10 +1297,8 @@ impl ::std::fmt::Display for ShaderCreationError {
ShaderCreationError::Io(ref err) => write!(f, "couldn't read shader: {}", err),
ShaderCreationError::Compile(ref _path, ref s) => {
write!(f, "failed compiling shader: {}", s)
},
ShaderCreationError::Link(ref s) => {
write!(f, "failed linking shader: {}", s)
},
}
ShaderCreationError::Link(ref s) => write!(f, "failed linking shader: {}", s),
}
}
}
@ -1262,7 +1309,6 @@ impl From<io::Error> for ShaderCreationError {
}
}
/// Manages a single texture atlas
///
/// The strategy for filling an atlas looks roughly like this:
@ -1332,7 +1378,7 @@ impl Atlas {
0,
gl::RGB,
gl::UNSIGNED_BYTE,
ptr::null()
ptr::null(),
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
@ -1407,7 +1453,7 @@ impl Atlas {
height,
gl::RGB,
gl::UNSIGNED_BYTE,
glyph.buf.as_ptr() as *const _
glyph.buf.as_ptr() as *const _,
);
gl::BindTexture(gl::TEXTURE_2D, 0);

45
src/tty/mod.rs Normal file
View File

@ -0,0 +1,45 @@
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! tty related functionality
use mio;
use std::io;
#[cfg(not(windows))]
mod unix;
#[cfg(not(windows))]
pub use self::unix::*;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use self::windows::*;
/// This trait defines the behaviour needed to read and/or write to a stream.
/// It defines an abstraction over mio's interface in order to allow either one
/// read/write object or a seperate read and write object.
pub trait EventedReadWrite {
type Reader: io::Read;
type Writer: io::Write;
fn register(&mut self, &mio::Poll, &mut Iterator<Item = &usize>, mio::Ready, mio::PollOpt) -> io::Result<()>;
fn reregister(&mut self, &mio::Poll, mio::Ready, mio::PollOpt) -> io::Result<()>;
fn deregister(&mut self, &mio::Poll) -> io::Result<()>;
fn reader(&mut self) -> &mut Self::Reader;
fn read_token(&self) -> mio::Token;
fn writer(&mut self) -> &mut Self::Writer;
fn write_token(&self) -> mio::Token;
}

View File

@ -14,20 +14,26 @@
//
//! tty related functionality
//!
use std::ffi::CStr;
use std::fs::File;
use std::os::unix::io::FromRawFd;
use std::os::unix::process::CommandExt;
use std::ptr;
use std::process::{Command, Stdio};
use libc::{self, winsize, c_int, pid_t, WNOHANG, SIGCHLD, TIOCSCTTY};
use terminfo::Database;
use tty::EventedReadWrite;
use term::SizeInfo;
use display::OnResize;
use config::{Config, Shell};
use cli::Options;
use mio;
use libc::{self, c_int, pid_t, winsize, SIGCHLD, TIOCSCTTY, WNOHANG};
use terminfo::Database;
use std::os::unix::io::{FromRawFd, RawFd};
use std::fs::File;
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};
use std::ffi::CStr;
use std::ptr;
use mio::unix::EventedFd;
use std::io;
use std::os::unix::io::AsRawFd;
/// Process ID of child process
@ -164,7 +170,6 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd {
die!("pw not found");
}
// sanity check
assert_eq!(entry.pw_uid, uid);
@ -180,8 +185,37 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd {
}
}
pub struct Pty {
pub fd: File,
pub raw_fd: RawFd,
token: mio::Token,
}
impl Pty {
/// Resize the pty
///
/// Tells the kernel that the window size changed with the new pixel
/// dimensions and line/column counts.
pub fn resize<T: ToWinsize>(&self, size: &T) {
let win = size.to_winsize();
let res = unsafe {
libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _)
};
if res < 0 {
die!("ioctl TIOCSWINSZ failed: {}", errno());
}
}
}
/// Create a new tty and return a handle to interact with it.
pub fn new<T: ToWinsize>(config: &Config, options: &Options, size: &T, window_id: Option<usize>) -> Pty {
pub fn new<T: ToWinsize>(
config: &Config,
options: &Options,
size: &T,
window_id: Option<usize>,
) -> Pty {
let win = size.to_winsize();
let mut buf = [0; 1024];
let pw = get_pw_entry(&mut buf);
@ -279,7 +313,11 @@ pub fn new<T: ToWinsize>(config: &Config, options: &Options, size: &T, window_id
set_nonblocking(master);
}
let pty = Pty { fd: master };
let pty = Pty {
fd: unsafe {File::from_raw_fd(master) },
raw_fd: master,
token: mio::Token::from(0)
};
pty.resize(size);
pty
},
@ -289,34 +327,60 @@ pub fn new<T: ToWinsize>(config: &Config, options: &Options, size: &T, window_id
}
}
pub struct Pty {
fd: c_int,
}
impl EventedReadWrite for Pty {
type Reader = File;
type Writer = File;
impl Pty {
/// Get reader for the TTY
///
/// XXX File is a bad abstraction here; it closes the fd on drop
pub fn reader(&self) -> File {
unsafe {
File::from_raw_fd(self.fd)
}
#[inline]
fn register(
&mut self,
poll: &mio::Poll,
token: &mut Iterator<Item = &usize>,
interest: mio::Ready,
poll_opts: mio::PollOpt,
) -> io::Result<()> {
self.token = (*token.next().unwrap()).into();
poll.register(
&EventedFd(&self.raw_fd),
self.token,
interest,
poll_opts
)
}
/// Resize the pty
///
/// Tells the kernel that the window size changed with the new pixel
/// dimensions and line/column counts.
pub fn resize<T: ToWinsize>(&self, size: &T) {
let win = size.to_winsize();
#[inline]
fn reregister(&mut self, poll: &mio::Poll, interest: mio::Ready, poll_opts: mio::PollOpt) -> io::Result<()> {
poll.reregister(
&EventedFd(&self.raw_fd),
self.token,
interest,
poll_opts
)
}
let res = unsafe {
libc::ioctl(self.fd, libc::TIOCSWINSZ, &win as *const _)
};
#[inline]
fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> {
poll.deregister(&EventedFd(&self.raw_fd))
}
if res < 0 {
die!("ioctl TIOCSWINSZ failed: {}", errno());
}
#[inline]
fn reader(&mut self) -> &mut File {
&mut self.fd
}
#[inline]
fn read_token(&self) -> mio::Token {
self.token
}
#[inline]
fn writer(&mut self) -> &mut File {
&mut self.fd
}
#[inline]
fn write_token(&self) -> mio::Token {
self.token
}
}
@ -337,14 +401,22 @@ impl<'a> ToWinsize for &'a SizeInfo {
}
}
impl OnResize for Pty {
impl OnResize for i32 {
fn on_resize(&mut self, size: &SizeInfo) {
self.resize(&size);
let win = size.to_winsize();
let res = unsafe {
libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _)
};
if res < 0 {
die!("ioctl TIOCSWINSZ failed: {}", errno());
}
}
}
unsafe fn set_nonblocking(fd: c_int) {
use libc::{fcntl, F_SETFL, F_GETFL, O_NONBLOCK};
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
let res = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
assert_eq!(res, 0);

284
src/tty/windows.rs Normal file
View File

@ -0,0 +1,284 @@
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io;
use std::fs::OpenOptions;
use std::os::raw::c_void;
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
use std::os::windows::fs::OpenOptionsExt;
use std::env;
use std::cell::UnsafeCell;
use dunce::canonicalize;
use mio;
use mio::Evented;
use mio_named_pipes::NamedPipe;
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::{WAIT_OBJECT_0, FILE_FLAG_OVERLAPPED};
use winapi::shared::winerror::WAIT_TIMEOUT;
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
use winpty::Config as WinptyConfig;
use config::{Config, Shell};
use display::OnResize;
use cli::Options;
use tty::EventedReadWrite;
use term::SizeInfo;
/// Handle to the winpty agent process. Required so we know when it closes.
static mut HANDLE: *mut c_void = 0usize as *mut c_void;
/// How long the winpty agent should wait for any RPC request
/// This is a placeholder value until we see how often long responses happen
const AGENT_TIMEOUT: u32 = 10000;
pub fn process_should_exit() -> bool {
unsafe {
match WaitForSingleObject(HANDLE, 0) {
// Process has exited
WAIT_OBJECT_0 => {
info!("wait_object_0");
true
}
// Reached timeout of 0, process has not exited
WAIT_TIMEOUT => false,
// Error checking process, winpty gave us a bad agent handle?
_ => {
info!("Bad exit: {}", ::std::io::Error::last_os_error());
true
}
}
}
}
pub struct Pty<'a, R: io::Read + Evented + Send, W: io::Write + Evented + Send> {
// TODO: Provide methods for accessing this safely
pub winpty: UnsafeCell<Winpty<'a>>,
conout: R,
conin: W,
read_token: mio::Token,
write_token: mio::Token,
}
pub fn new<'a>(
config: &Config,
options: &Options,
size: &SizeInfo,
_window_id: Option<usize>,
) -> Pty<'a, NamedPipe, NamedPipe> {
// Create config
let mut wconfig = WinptyConfig::new(ConfigFlags::empty()).unwrap();
wconfig.set_initial_size(size.cols().0 as i32, size.lines().0 as i32);
wconfig.set_mouse_mode(&MouseMode::Auto);
wconfig.set_agent_timeout(AGENT_TIMEOUT);
// Start agent
let mut winpty = Winpty::open(&wconfig).unwrap();
let (conin, conout) = (winpty.conin_name(), winpty.conout_name());
// Get process commandline
let default_shell = &Shell::new(env::var("COMSPEC").unwrap_or_else(|_| "cmd".into()));
let shell = config.shell().unwrap_or(default_shell);
let initial_command = options.command().unwrap_or(shell);
let mut cmdline = initial_command.args().to_vec();
cmdline.insert(0, initial_command.program().into());
// Warning, here be borrow hell
let cwd = options.working_dir.as_ref().map(|dir| canonicalize(dir).unwrap());
let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());
// Spawn process
let spawnconfig = SpawnConfig::new(
SpawnFlags::AUTO_SHUTDOWN | SpawnFlags::EXIT_AFTER_SHUTDOWN,
None, // appname
Some(&cmdline.join(" ")),
cwd,
None, // Env
).unwrap();
let default_opts = &mut OpenOptions::new();
default_opts
.share_mode(0)
.custom_flags(FILE_FLAG_OVERLAPPED);
let (conout_pipe, conin_pipe);
unsafe {
conout_pipe = NamedPipe::from_raw_handle(
default_opts
.clone()
.read(true)
.open(conout)
.unwrap()
.into_raw_handle(),
);
conin_pipe = NamedPipe::from_raw_handle(
default_opts
.clone()
.write(true)
.open(conin)
.unwrap()
.into_raw_handle(),
);
};
if let Some(err) = conout_pipe.connect().err() {
if err.kind() != io::ErrorKind::WouldBlock {
panic!(err);
}
}
assert!(conout_pipe.take_error().unwrap().is_none());
if let Some(err) = conin_pipe.connect().err() {
if err.kind() != io::ErrorKind::WouldBlock {
panic!(err);
}
}
assert!(conin_pipe.take_error().unwrap().is_none());
winpty.spawn(&spawnconfig).unwrap();
unsafe {
HANDLE = winpty.raw_handle();
}
Pty {
winpty: UnsafeCell::new(winpty),
conout: conout_pipe,
conin: conin_pipe,
// Placeholder tokens that are overwritten
read_token: 0.into(),
write_token: 0.into(),
}
}
impl<'a> EventedReadWrite for Pty<'a, NamedPipe, NamedPipe> {
type Reader = NamedPipe;
type Writer = NamedPipe;
#[inline]
fn register(
&mut self,
poll: &mio::Poll,
token: &mut Iterator<Item = &usize>,
interest: mio::Ready,
poll_opts: mio::PollOpt,
) -> io::Result<()> {
self.read_token = (*token.next().unwrap()).into();
self.write_token = (*token.next().unwrap()).into();
if interest.is_readable() {
poll.register(
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?
} else {
poll.register(
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?
}
if interest.is_writable() {
poll.register(
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?
} else {
poll.register(
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?
}
Ok(())
}
#[inline]
fn reregister(&mut self, poll: &mio::Poll, interest: mio::Ready, poll_opts: mio::PollOpt) -> io::Result<()> {
if interest.is_readable() {
poll.reregister(
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?;
} else {
poll.reregister(
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?;
}
if interest.is_writable() {
poll.reregister(
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?;
} else {
poll.reregister(
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?;
}
Ok(())
}
#[inline]
fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> {
poll.deregister(&self.conout)?;
poll.deregister(&self.conin)?;
Ok(())
}
#[inline]
fn reader(&mut self) -> &mut NamedPipe {
&mut self.conout
}
#[inline]
fn read_token(&self) -> mio::Token {
self.read_token
}
#[inline]
fn writer(&mut self) -> &mut NamedPipe {
&mut self.conin
}
#[inline]
fn write_token(&self) -> mio::Token {
self.write_token
}
}
impl<'a> OnResize for Winpty<'a> {
fn on_resize(&mut self, sizeinfo: &SizeInfo) {
if sizeinfo.cols().0 > 0 && sizeinfo.lines().0 > 0 {
self.set_size(sizeinfo.cols().0, sizeinfo.lines().0)
.unwrap_or_else(|_| info!("Unable to set winpty size, did it die?"));
}
}
}

View File

@ -17,6 +17,10 @@ use std::ops::Deref;
use gl;
use glutin::GlContext;
#[cfg(windows)]
use winit::Icon;
#[cfg(windows)]
use image::ImageFormat;
use glutin::{
self, ContextBuilder, ControlFlow, CursorState, Event, EventsLoop,
MouseCursor as GlutinMouseCursor, WindowBuilder,
@ -27,6 +31,9 @@ use MouseCursor;
use cli::Options;
use config::{Decorations, WindowConfig};
#[cfg(windows)]
static WINDOW_ICON: &'static [u8] = include_bytes!("../assets/windows/alacritty.ico");
/// Default text for the window's title bar, if not overriden.
///
/// In X11, this the default value for the `WM_NAME` property.
@ -215,7 +222,7 @@ impl Window {
let event_loop = EventsLoop::new();
let title = options.title.as_ref().map_or(DEFAULT_TITLE, |t| t);
let class = options.class.as_ref().map_or(DEFAULT_CLASS, |c| c);
let class = options.class.as_ref().map_or(DEFAULT_TITLE, |c| c);
let window_builder = Window::get_platform_window(title, window_config);
let window_builder = Window::platform_builder_ext(window_builder, &class);
let window = create_gl_window(window_builder.clone(), &event_loop, false)
@ -225,14 +232,14 @@ impl Window {
// Text cursor
window.set_cursor(GlutinMouseCursor::Text);
// Set OpenGL symbol loader
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
// Make the context current so OpenGL operations can run
unsafe {
window.make_current()?;
}
// Set OpenGL symbol loader. This call MUST be after window.make_current on windows.
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let window = Window {
event_loop,
window,
@ -304,8 +311,11 @@ impl Window {
/// Set the window title
#[inline]
pub fn set_title(&self, title: &str) {
self.window.set_title(title);
pub fn set_title(&self, _title: &str) {
// Because winpty doesn't know anything about OSC escapes this gets set to an empty
// string on windows
#[cfg(not(windows))]
self.window.set_title(_title);
}
#[inline]
@ -357,7 +367,7 @@ impl Window {
window_builder
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(any(target_os = "macos", windows)))]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
let decorations = match window_config.decorations() {
Decorations::None => false,
@ -371,6 +381,23 @@ impl Window {
.with_decorations(decorations)
}
#[cfg(windows)]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap();
let decorations = match window_config.decorations() {
Decorations::None => false,
_ => true,
};
WindowBuilder::new()
.with_title(title)
.with_visibility(cfg!(windows))
.with_decorations(decorations)
.with_transparency(true)
.with_window_icon(Some(icon))
}
#[cfg(target_os = "macos")]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
use glutin::os::macos::WindowBuilderExt;
@ -421,11 +448,13 @@ impl Window {
)]
pub fn set_urgent(&self, _is_urgent: bool) {}
pub fn set_ime_spot(&self, x: i32, y: i32) {
self.window.set_ime_spot(x, y);
pub fn set_ime_spot(&self, _x: i32, _y: i32) {
// This is not implemented on windows as of winit 0.15.1
#[cfg(not(windows))]
self.window.set_ime_spot(_x, _y);
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
pub fn get_window_id(&self) -> Option<usize> {
use glutin::os::unix::WindowExt;
@ -435,7 +464,7 @@ impl Window {
}
}
#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn get_window_id(&self) -> Option<usize> {
None
}

577
winpty/Cargo.lock generated Normal file
View File

@ -0,0 +1,577 @@
[[package]]
name = "aho-corasick"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bindgen"
version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"clang-sys 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cexpr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clang-sys"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-deque"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-epoch"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "either"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "humantime"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libloading"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memoffset"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "named_pipe"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nodrop"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nom"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num_cpus"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quick-error"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon-core"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scopeguard"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "termcolor"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ucd-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "which"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "widestring"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wincolor"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winpty"
version = "0.1.0"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winpty-sys 1.1.0 (git+https://github.com/zacps/winpty?branch=rust)",
]
[[package]]
name = "winpty-sys"
version = "1.1.0"
source = "git+https://github.com/zacps/winpty?branch=rust#be5fd7323b588b81294b1d0a670d2dbd5322ff00"
dependencies = [
"bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0ba20154ea1f47ce2793322f049c5646cc6d0fa9759d5f333f286e507bf8080"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1"
"checksum bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)" = "603ed8d8392ace9581e834e26bd09799bf1e989a79bd1aedbb893e72962bdc6e"
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"
"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e"
"checksum clang-sys 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "939a1a34310b120d26eba35c29475933128b0ec58e24b43327f8dbe6036fc538"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
"checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
"checksum named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ed10a5ac4f5f7e5d75552b12c1d5d542debca81e573279dd1e4c19fde6efa6d"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1"
"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
"checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2"
"checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
"checksum winpty-sys 1.1.0 (git+https://github.com/zacps/winpty?branch=rust)" = "<none>"

16
winpty/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "winpty"
version = "0.1.0"
authors = ["Zac Pullar-Strecker <zacmps@gmail.com>"]
license = "MIT"
description = "Safe rust bindings for winpty"
[dependencies]
# TODO: Replace with official repo
winpty-sys = { git = "https://github.com/zacps/winpty", branch = "rust" }
bitflags = "1.0"
widestring = "0.2.2"
[dev-dependencies]
named_pipe = "0.3"
winapi = { version = "0.3", features = ["winnt", "processthreadsapi"] }

12
winpty/build.rs Normal file
View File

@ -0,0 +1,12 @@
use std::fs::copy;
use std::path::Path;
fn main() {
// The working directory for `cargo test` is in the deps folder, not the debug/release root
if cfg!(test) && Path::new("target").exists() {
#[cfg(debug_assertions)]
copy("../assets/windows/x86_64/winpty-agent.exe", "target/debug/deps/winpty-agent.exe").unwrap();
#[cfg(not(debug_assertions))]
copy("../assets/windows/x86_64/winpty-agent.exe", "target/release/deps/winpty-agent.exe").unwrap();
}
}

476
winpty/src/lib.rs Normal file
View File

@ -0,0 +1,476 @@
#![cfg_attr(feature = "cargo-clippy", deny(clippy, if_not_else, enum_glob_use, wrong_pub_self_convention))]
#[macro_use]
extern crate bitflags;
extern crate widestring;
extern crate winpty_sys;
use std::error::Error;
use std::fmt;
use std::path::PathBuf;
use std::result::Result;
use std::os::windows::io::RawHandle;
use std::ptr::{null, null_mut};
use fmt::{Display, Formatter};
use winpty_sys::*;
use widestring::WideCString;
pub enum ErrorCodes {
Success,
OutOfMemory,
SpawnCreateProcessFailed,
LostConnection,
AgentExeMissing,
Unspecified,
AgentDied,
AgentTimeout,
AgentCreationFailed,
}
pub enum MouseMode {
None,
Auto,
Force,
}
bitflags!(
pub struct SpawnFlags: u64 {
const AUTO_SHUTDOWN = 0x1;
const EXIT_AFTER_SHUTDOWN = 0x2;
}
);
bitflags!(
pub struct ConfigFlags: u64 {
const CONERR = 0x1;
const PLAIN_OUTPUT = 0x2;
const COLOR_ESCAPES = 0x4;
}
);
#[derive(Debug)]
pub struct Err<'a> {
ptr: &'a mut winpty_error_t,
code: u32,
message: String,
}
// Check to see whether winpty gave us an error
fn check_err<'a>(e: *mut winpty_error_t) -> Option<Err<'a>> {
let err = unsafe {
let raw = winpty_error_msg(e);
Err {
ptr: &mut *e,
code: winpty_error_code(e),
message: String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))),
}
};
if err.code == 0 {
None
} else {
Some(err)
}
}
impl<'a> Drop for Err<'a> {
fn drop(&mut self) {
unsafe {
winpty_error_free(self.ptr);
}
}
}
impl<'a> Display for Err<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "Code: {}, Message: {}", self.code, self.message)
}
}
impl<'a> Error for Err<'a> {
fn description(&self) -> &str {
&self.message
}
}
#[derive(Debug)]
/// Winpty agent config
pub struct Config<'a>(&'a mut winpty_config_t);
impl<'a, 'b> Config<'a> {
pub fn new(flags: ConfigFlags) -> Result<Self, Err<'b>> {
let mut err = null_mut() as *mut winpty_error_t;
let config = unsafe { winpty_config_new(flags.bits(), &mut err) };
if let Some(err) = check_err(err) {
Result::Err(err)
} else {
unsafe { Ok(Config(&mut *config)) }
}
}
/// Set the initial size of the console window
pub fn set_initial_size(&mut self, cols: i32, rows: i32) {
unsafe {
winpty_config_set_initial_size(self.0, cols, rows);
}
}
/// Set the mouse mode
pub fn set_mouse_mode(&mut self, mode: &MouseMode) {
let m = match mode {
MouseMode::None => 0,
MouseMode::Auto => 1,
MouseMode::Force => 2,
};
unsafe {
winpty_config_set_mouse_mode(self.0, m);
}
}
/// Amount of time to wait for the agent to startup and to wait for any given
/// agent RPC request. Must be greater than 0. Can be INFINITE.
// Might be a better way to represent this while still retaining infinite capability?
// Enum?
pub fn set_agent_timeout(&mut self, timeout: u32) {
unsafe {
winpty_config_set_agent_timeout(self.0, timeout);
}
}
}
impl<'a> Drop for Config<'a> {
fn drop(&mut self) {
unsafe {
winpty_config_free(self.0);
}
}
}
#[derive(Debug)]
/// A struct representing the winpty agent process
pub struct Winpty<'a>(&'a mut winpty_t);
impl<'a, 'b> Winpty<'a> {
/// Starts the agent. This process will connect to the agent
/// over a control pipe, and the agent will open data pipes
/// (e.g. CONIN and CONOUT).
pub fn open(cfg: &Config) -> Result<Self, Err<'b>> {
let mut err = null_mut() as *mut winpty_error_t;
unsafe {
let winpty = winpty_open(cfg.0, &mut err);
let err = check_err(err);
if let Some(err) = err {
Result::Err(err)
} else {
Ok(Winpty(&mut *winpty))
}
}
}
/// Returns the handle to the winpty agent process
pub fn raw_handle(&mut self) -> RawHandle {
unsafe { winpty_agent_process(self.0) }
}
/// Returns the name of the input pipe.
/// Pipe is half-duplex.
pub fn conin_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conin_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
}
}
/// Returns the name of the output pipe.
/// Pipe is half-duplex.
pub fn conout_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conout_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
}
}
/// Returns the name of the error pipe.
/// The name will only be valid if ConfigFlags::CONERR was specified.
/// Pipe is half-duplex.
pub fn conerr_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conerr_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
}
}
/// Change the size of the Windows console window.
///
/// cols & rows MUST be greater than 0
pub fn set_size(&mut self, cols: usize, rows: usize) -> Result<(), Err> {
assert!(cols > 0 && rows > 0);
let mut err = null_mut() as *mut winpty_error_t;
unsafe {
winpty_set_size(self.0, cols as i32, rows as i32, &mut err);
}
if let Some(err) = check_err(err) {
Result::Err(err)
} else {
Ok(())
}
}
/// Get the list of processses running in the winpty agent. Returns <= count processes
///
/// `count` must be greater than 0. Larger values cause a larger allocation.
// TODO: This should return Vec<Handle> instead of Vec<i32>
pub fn console_process_list(&mut self, count: usize) -> Result<Vec<i32>, Err> {
assert!(count > 0);
let mut err = null_mut() as *mut winpty_error_t;
let mut process_list = Vec::with_capacity(count);
unsafe {
let len = winpty_get_console_process_list(self.0, process_list.as_mut_ptr(), count as i32, &mut err) as usize;
process_list.set_len(len);
}
if let Some(err) = check_err(err) {
Result::Err(err)
} else {
Ok(process_list)
}
}
/// Spawns the new process.
///
/// spawn can only be called once per Winpty object. If it is called
/// before the output data pipe(s) is/are connected, then collected output is
/// buffered until the pipes are connected, rather than being discarded.
/// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
// TODO: Support getting the process and thread handle of the spawned process (Not the agent)
// TODO: Support returning the error from CreateProcess
pub fn spawn(
&mut self,
cfg: &SpawnConfig,
) -> Result<(), Err> {
let mut err = null_mut() as *mut winpty_error_t;
unsafe {
let ok = winpty_spawn(
self.0,
cfg.0 as *const winpty_spawn_config_s,
null_mut(), // Process handle
null_mut(), // Thread handle
null_mut(), // Create process error
&mut err,
);
if ok == 0 { return Ok(());}
}
if let Some(err) = check_err(err) {
Result::Err(err)
} else {
Ok(())
}
}
}
// winpty_t is thread-safe
unsafe impl<'a> Sync for Winpty<'a> {}
unsafe impl<'a> Send for Winpty<'a> {}
impl<'a> Drop for Winpty<'a> {
fn drop(&mut self) {
unsafe {
winpty_free(self.0);
}
}
}
#[derive(Debug)]
/// Information about a process for winpty to spawn
pub struct SpawnConfig<'a>(&'a mut winpty_spawn_config_t);
impl<'a, 'b> SpawnConfig<'a> {
/// Creates a new spawnconfig
pub fn new(
spawnflags: SpawnFlags,
appname: Option<&str>,
cmdline: Option<&str>,
cwd: Option<&str>,
end: Option<&str>,
) -> Result<Self, Err<'b>> {
let mut err = null_mut() as *mut winpty_error_t;
let (appname, cmdline, cwd, end) = (
appname.map_or(null(), |s| WideCString::from_str(s).unwrap().into_raw()),
cmdline.map_or(null(), |s| WideCString::from_str(s).unwrap().into_raw()),
cwd.map_or(null(), |s| WideCString::from_str(s).unwrap().into_raw()),
end.map_or(null(), |s| WideCString::from_str(s).unwrap().into_raw()),
);
let spawn_config = unsafe {
winpty_spawn_config_new(spawnflags.bits(), appname, cmdline, cwd, end, &mut err)
};
// Required to free the strings
unsafe {
if !appname.is_null() {
WideCString::from_raw(appname as *mut u16);
}
if !cmdline.is_null() {
WideCString::from_raw(cmdline as *mut u16);
}
if !cwd.is_null() {
WideCString::from_raw(cwd as *mut u16);
}
if !end.is_null() {
WideCString::from_raw(end as *mut u16);
}
}
if let Some(err) = check_err(err) {
Result::Err(err)
} else {
unsafe { Ok(SpawnConfig(&mut *spawn_config)) }
}
}
}
impl<'a> Drop for SpawnConfig<'a> {
fn drop(&mut self) {
unsafe {
winpty_spawn_config_free(self.0);
}
}
}
#[cfg(test)]
mod tests {
extern crate named_pipe;
extern crate winapi;
use self::named_pipe::PipeClient;
use self::winapi::um::processthreadsapi::OpenProcess;
use self::winapi::um::winnt::READ_CONTROL;
use std::ptr::null_mut;
use {Config, ConfigFlags, SpawnConfig, SpawnFlags, Winpty};
#[test]
// Test that we can start a process in winpty
fn spawn_process() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
}
#[test]
// Test that pipes connected before winpty is spawned can be connected to
fn valid_pipe_connect_before() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
// Check we can connect to both pipes
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe");
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
}
#[test]
// Test that pipes connected after winpty is spawned can be connected to
fn valid_pipe_connect_after() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
// Check we can connect to both pipes
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe");
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
}
#[test]
fn resize() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty.set_size(1, 1).unwrap();
}
#[test]
// Test that each id returned by cosole_process_list points to an actual process
fn console_process_list_valid() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
let processes = winpty.console_process_list(1000).expect("failed to get console process list");
// Check that each id is valid
processes.iter().for_each(|id| {
let handle = unsafe {
OpenProcess(
READ_CONTROL, // permissions
false as i32, // inheret
*id as u32
)
};
assert!(handle != null_mut());
});
}
}