curses 1.5.4 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c363e2ee899bf325f21d679cdf68e78caec9959d8ae8d631e51aaefdf361470
4
- data.tar.gz: f12600ce5a6c645604cc74813def3c0afa9fed11c2e4117f4c53d374b3b77018
3
+ metadata.gz: fb2bc3cbe268313c253f41ee1a5e5bbabe504fa69e7631fae89a0865f1298125
4
+ data.tar.gz: a534a9ec711119fd1739c5df302167efc550fc089b346ab89b97004096cce060
5
5
  SHA512:
6
- metadata.gz: ee80959584814c5102f2e2e881c3da795833d4c682cd952b5484ad11d2aa14130916fef00fea1d42d345d3d75a1ced642fd0bb9b839414fa90615cace0ad34f8
7
- data.tar.gz: de4842e3f63642306b6a914084d5979c12d81263eeffdf82f2b0c56fc1da8c3ccadb400971a8541811d0244c456428f42fb6e5c20655fb163e77f5571ec8a1c4
6
+ metadata.gz: 8be3f5ce383b3626e8cd3a2800408bb6fbc8abc98a24e13bd365d7e74e93c49aff2f2501d73a2db584bcd7250f2834215411cd1ab7511c7c4fd8a4457e55e56a
7
+ data.tar.gz: e67a948df315465f11539eda4bd9f751272c02a72bfa68b0546ade9b7af86d0f097ccebc9b9b318449958362ba99d748a23b63de76664b581e2ce675db509347
@@ -0,0 +1,132 @@
1
+ FROM node:20
2
+
3
+ ARG TZ
4
+ ENV TZ="$TZ"
5
+
6
+ ARG CLAUDE_CODE_VERSION=latest
7
+
8
+ # Install GitHub CLI from official repository
9
+ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
10
+ | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
11
+ && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
12
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
13
+ > /etc/apt/sources.list.d/github-cli.list
14
+
15
+ # Install basic development tools and iptables/ipset
16
+ RUN apt-get update && apt-get install -y --no-install-recommends \
17
+ less \
18
+ git \
19
+ procps \
20
+ sudo \
21
+ fzf \
22
+ zsh \
23
+ man-db \
24
+ unzip \
25
+ gnupg2 \
26
+ gh \
27
+ iptables \
28
+ ipset \
29
+ iproute2 \
30
+ dnsutils \
31
+ aggregate \
32
+ jq \
33
+ nano \
34
+ vim \
35
+ locales \
36
+ # Ruby build dependencies
37
+ build-essential \
38
+ libssl-dev \
39
+ libreadline-dev \
40
+ zlib1g-dev \
41
+ libyaml-dev \
42
+ libffi-dev \
43
+ libncurses-dev \
44
+ aspell \
45
+ aspell-en \
46
+ aspell-fr \
47
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
48
+
49
+ # Configure locale
50
+ RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
51
+ locale-gen en_US.UTF-8
52
+
53
+ ENV LANG=en_US.UTF-8
54
+ ENV LC_ALL=en_US.UTF-8
55
+
56
+ # Ensure default node user has access to /usr/local/share
57
+ RUN mkdir -p /usr/local/share/npm-global && \
58
+ chown -R node:node /usr/local/share
59
+
60
+ ARG USERNAME=node
61
+
62
+ # Persist bash history.
63
+ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
64
+ && mkdir /commandhistory \
65
+ && touch /commandhistory/.bash_history \
66
+ && chown -R $USERNAME /commandhistory
67
+
68
+ # Set `DEVCONTAINER` environment variable to help with orientation
69
+ ENV DEVCONTAINER=true
70
+
71
+ # Create workspace and config directories and set permissions
72
+ RUN mkdir -p /workspace /home/node/.claude /home/node/.config/gh && \
73
+ chown -R node:node /workspace /home/node/.claude /home/node/.config/gh
74
+
75
+ WORKDIR /workspace
76
+
77
+ ARG GIT_DELTA_VERSION=0.18.2
78
+ RUN ARCH=$(dpkg --print-architecture) && \
79
+ wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
80
+ sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
81
+ rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
82
+
83
+ # Set up non-root user
84
+ USER node
85
+
86
+ # Install rbenv and ruby-build
87
+ ARG RUBY_VERSION=4.0.1
88
+ RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv && \
89
+ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build && \
90
+ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshrc && \
91
+ echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc && \
92
+ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc && \
93
+ echo 'eval "$(rbenv init - bash)"' >> ~/.bashrc
94
+
95
+ # Install Ruby
96
+ ENV PATH="/home/node/.rbenv/bin:/home/node/.rbenv/shims:$PATH"
97
+ RUN rbenv install ${RUBY_VERSION} && \
98
+ rbenv global ${RUBY_VERSION} && \
99
+ gem install bundler
100
+
101
+ # Install global packages
102
+ ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
103
+ ENV PATH=$PATH:/usr/local/share/npm-global/bin
104
+
105
+ # Set the default shell to zsh rather than sh
106
+ ENV SHELL=/bin/zsh
107
+
108
+ # Set the default editor and visual
109
+ ENV EDITOR=nano
110
+ ENV VISUAL=nano
111
+
112
+ # Default powerline10k theme
113
+ ARG ZSH_IN_DOCKER_VERSION=1.2.0
114
+ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
115
+ -p git \
116
+ -p fzf \
117
+ -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
118
+ -a "source /usr/share/doc/fzf/examples/completion.zsh" \
119
+ -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
120
+ -x
121
+
122
+ # Install Claude
123
+ RUN curl -fsSL https://claude.ai/install.sh | bash
124
+
125
+
126
+ # Copy and set up firewall script
127
+ COPY init-firewall.sh /usr/local/bin/
128
+ USER root
129
+ RUN chmod +x /usr/local/bin/init-firewall.sh && \
130
+ echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
131
+ chmod 0440 /etc/sudoers.d/node-firewall
132
+ USER node
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "Claude Code Sandbox",
3
+ "build": {
4
+ "dockerfile": "Dockerfile",
5
+ "args": {
6
+ "TZ": "${localEnv:TZ:America/Los_Angeles}",
7
+ "CLAUDE_CODE_VERSION": "latest",
8
+ "GIT_DELTA_VERSION": "0.18.2",
9
+ "ZSH_IN_DOCKER_VERSION": "1.2.0"
10
+ }
11
+ },
12
+ "runArgs": [
13
+ "--cap-add=NET_ADMIN",
14
+ "--cap-add=NET_RAW"
15
+ ],
16
+ "customizations": {
17
+ "vscode": {
18
+ "extensions": [
19
+ "anthropic.claude-code",
20
+ "dbaeumer.vscode-eslint",
21
+ "esbenp.prettier-vscode",
22
+ "eamodio.gitlens"
23
+ ],
24
+ "settings": {
25
+ "editor.formatOnSave": true,
26
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
27
+ "editor.codeActionsOnSave": {
28
+ "source.fixAll.eslint": "explicit"
29
+ },
30
+ "terminal.integrated.defaultProfile.linux": "zsh",
31
+ "terminal.integrated.profiles.linux": {
32
+ "bash": {
33
+ "path": "bash",
34
+ "icon": "terminal-bash"
35
+ },
36
+ "zsh": {
37
+ "path": "zsh"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ },
43
+ "remoteUser": "node",
44
+ "mounts": [
45
+ "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
46
+ "source=claude-code-config,target=/home/node/.claude,type=volume",
47
+ "source=claude-code-gh-config,target=/home/node/.config/gh,type=volume"
48
+ ],
49
+ "containerEnv": {
50
+ "NODE_OPTIONS": "--max-old-space-size=4096",
51
+ "CLAUDE_CONFIG_DIR": "/home/node/.claude",
52
+ "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
53
+ },
54
+ "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
55
+ "workspaceFolder": "/workspace",
56
+ "postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
57
+ "waitFor": "postStartCommand"
58
+ }
@@ -0,0 +1,139 @@
1
+ #!/bin/bash
2
+ set -euo pipefail # Exit on error, undefined vars, and pipeline failures
3
+ IFS=$'\n\t' # Stricter word splitting
4
+
5
+ # 1. Extract Docker DNS info BEFORE any flushing
6
+ DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
7
+
8
+ # Flush existing rules and delete existing ipsets
9
+ iptables -F
10
+ iptables -X
11
+ iptables -t nat -F
12
+ iptables -t nat -X
13
+ iptables -t mangle -F
14
+ iptables -t mangle -X
15
+ ipset destroy allowed-domains 2>/dev/null || true
16
+
17
+ # 2. Selectively restore ONLY internal Docker DNS resolution
18
+ if [ -n "$DOCKER_DNS_RULES" ]; then
19
+ echo "Restoring Docker DNS rules..."
20
+ iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
21
+ iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
22
+ echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
23
+ else
24
+ echo "No Docker DNS rules to restore"
25
+ fi
26
+
27
+ # First allow DNS and localhost before any restrictions
28
+ # Allow outbound DNS
29
+ iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
30
+ # Allow inbound DNS responses
31
+ iptables -A INPUT -p udp --sport 53 -j ACCEPT
32
+ # Allow outbound SSH
33
+ iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
34
+ # Allow inbound SSH responses
35
+ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
36
+ # Allow localhost
37
+ iptables -A INPUT -i lo -j ACCEPT
38
+ iptables -A OUTPUT -o lo -j ACCEPT
39
+
40
+ # Create ipset with CIDR support
41
+ ipset create allowed-domains hash:net
42
+
43
+ # Fetch GitHub meta information and aggregate + add their IP ranges
44
+ echo "Fetching GitHub IP ranges..."
45
+ gh_ranges=$(curl -s https://api.github.com/meta)
46
+ if [ -z "$gh_ranges" ]; then
47
+ echo "ERROR: Failed to fetch GitHub IP ranges"
48
+ exit 1
49
+ fi
50
+
51
+ if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
52
+ echo "ERROR: GitHub API response missing required fields"
53
+ exit 1
54
+ fi
55
+
56
+ echo "Processing GitHub IPs..."
57
+ while read -r cidr; do
58
+ if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
59
+ echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
60
+ exit 1
61
+ fi
62
+ echo "Adding GitHub range $cidr"
63
+ ipset add allowed-domains "$cidr"
64
+ done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
65
+
66
+ # Resolve and add other allowed domains
67
+ for domain in \
68
+ "storage.googleapis.com" \
69
+ "registry.npmjs.org" \
70
+ "api.anthropic.com" \
71
+ "sentry.io" \
72
+ "statsig.anthropic.com" \
73
+ "statsig.com" \
74
+ "marketplace.visualstudio.com" \
75
+ "vscode.blob.core.windows.net" \
76
+ "update.code.visualstudio.com" \
77
+ "rubygems.org"; do
78
+ echo "Resolving $domain..."
79
+ ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
80
+ if [ -z "$ips" ]; then
81
+ echo "ERROR: Failed to resolve $domain"
82
+ exit 1
83
+ fi
84
+
85
+ while read -r ip; do
86
+ if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
87
+ echo "ERROR: Invalid IP from DNS for $domain: $ip"
88
+ exit 1
89
+ fi
90
+ echo "Adding $ip for $domain"
91
+ ipset add allowed-domains "$ip"
92
+ done < <(echo "$ips")
93
+ done
94
+
95
+ # Get host IP from default route
96
+ HOST_IP=$(ip route | grep default | cut -d" " -f3)
97
+ if [ -z "$HOST_IP" ]; then
98
+ echo "ERROR: Failed to detect host IP"
99
+ exit 1
100
+ fi
101
+
102
+ HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
103
+ echo "Host network detected as: $HOST_NETWORK"
104
+
105
+ # Set up remaining iptables rules
106
+ iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
107
+ iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
108
+
109
+ # Set default policies to DROP first
110
+ iptables -P INPUT DROP
111
+ iptables -P FORWARD DROP
112
+ iptables -P OUTPUT DROP
113
+
114
+ # First allow established connections for already approved traffic
115
+ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
116
+ iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
117
+
118
+ # Then allow only specific outbound traffic to allowed domains
119
+ iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
120
+
121
+ # Explicitly REJECT all other outbound traffic for immediate feedback
122
+ iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
123
+
124
+ echo "Firewall configuration complete"
125
+ echo "Verifying firewall rules..."
126
+ if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
127
+ echo "ERROR: Firewall verification failed - was able to reach https://example.com"
128
+ exit 1
129
+ else
130
+ echo "Firewall verification passed - unable to reach https://example.com as expected"
131
+ fi
132
+
133
+ # Verify GitHub API access
134
+ if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
135
+ echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
136
+ exit 1
137
+ else
138
+ echo "Firewall verification passed - able to reach https://api.github.com as expected"
139
+ fi
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  steps:
21
21
  - name: Checkout
22
- uses: actions/checkout@v6
22
+ uses: actions/checkout@v7
23
23
  - name: Setup Ruby
24
24
  uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
25
25
  with:
@@ -27,14 +27,14 @@ jobs:
27
27
  bundler-cache: true
28
28
  - name: Setup Pages
29
29
  id: pages
30
- uses: actions/configure-pages@v5
30
+ uses: actions/configure-pages@v6
31
31
  - name: Install dependencies
32
32
  run: gem install rdoc
33
33
  - name: Build with RDoc
34
34
  # Outputs to the './_site' directory by default
35
35
  run: rake rdoc
36
36
  - name: Upload artifact
37
- uses: actions/upload-pages-artifact@v4
37
+ uses: actions/upload-pages-artifact@v5
38
38
 
39
39
  deploy:
40
40
  environment:
@@ -45,4 +45,4 @@ jobs:
45
45
  steps:
46
46
  - name: Deploy to GitHub Pages
47
47
  id: deployment
48
- uses: actions/deploy-pages@v4
48
+ uses: actions/deploy-pages@v5
@@ -1,12 +1,15 @@
1
1
  name: macos
2
2
 
3
- on: [push]
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
4
7
 
5
8
  jobs:
6
9
  build:
7
10
  runs-on: macos-latest
8
11
  steps:
9
- - uses: actions/checkout@v6
12
+ - uses: actions/checkout@v7
10
13
  - uses: ruby/setup-ruby@v1
11
14
  with:
12
15
  ruby-version: 3.4
@@ -24,11 +24,11 @@ jobs:
24
24
  steps:
25
25
  # Set up
26
26
  - name: Harden Runner
27
- uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
27
+ uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
28
28
  with:
29
29
  egress-policy: audit
30
30
 
31
- - uses: actions/checkout@v6
31
+ - uses: actions/checkout@v7
32
32
  with:
33
33
  submodules: true
34
34
 
@@ -38,6 +38,11 @@ jobs:
38
38
  bundler-cache: true
39
39
  ruby-version: ruby
40
40
 
41
+ # Prevent `git fetch --tags` in release-gem from fetching submodule
42
+ # commits unreachable in the current submodule remote
43
+ - name: Disable submodule fetch recursion
44
+ run: git config fetch.recurseSubmodules false
45
+
41
46
  # Release
42
47
  - name: Publish to RubyGems
43
48
  uses: rubygems/release-gem@v1
@@ -1,6 +1,9 @@
1
1
  name: ubuntu
2
2
 
3
- on: [push]
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
4
7
 
5
8
  jobs:
6
9
  test:
@@ -9,7 +12,7 @@ jobs:
9
12
  ruby: [ head, 3.4, 3.3, 3.2 ]
10
13
  runs-on: ubuntu-latest
11
14
  steps:
12
- - uses: actions/checkout@v6
15
+ - uses: actions/checkout@v7
13
16
  - uses: ruby/setup-ruby@v1
14
17
  with:
15
18
  ruby-version: ${{ matrix.ruby }}
@@ -1,6 +1,9 @@
1
1
  name: windows
2
2
 
3
- on: [push]
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
4
7
 
5
8
  jobs:
6
9
  build:
@@ -9,7 +12,7 @@ jobs:
9
12
  matrix:
10
13
  ruby: [ 'mingw', 'mswin', '3.4' ]
11
14
  steps:
12
- - uses: actions/checkout@v6
15
+ - uses: actions/checkout@v7
13
16
  with:
14
17
  submodules: true
15
18
  - name: Set up Ruby
data/README.md CHANGED
@@ -53,7 +53,7 @@ Type the following command, and see `[rdoc]` of curses:
53
53
 
54
54
  ## Limitations
55
55
 
56
- * curses.gem doesn't support more than 256 color pairs. See https://reversed.top/2019-02-05/more-than-256-curses-color-pairs/ for details.
56
+ * On ncurses 6+ compiled with extended color support, more than 256 color pairs are supported transparently via the extended colors API (`init_extended_pair`, etc.). Use `Curses.support_extended_colors?` to check at runtime.
57
57
 
58
58
  ## Developers
59
59
 
data/curses.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new { |s|
2
2
  s.name = "curses"
3
- s.version = "1.5.4"
3
+ s.version = "1.6.1"
4
4
  s.author = ["Shugo Maeda", 'Eric Hodel']
5
5
  s.email = ["shugo@ruby-lang.org", 'drbrain@segment7.net']
6
6
  s.homepage = "https://github.com/ruby/curses"
data/ext/curses/curses.c CHANGED
@@ -1321,7 +1321,11 @@ curses_init_pair(VALUE obj, VALUE pair, VALUE f, VALUE b)
1321
1321
  {
1322
1322
  /* may have to raise exception on ERR */
1323
1323
  curses_stdscr();
1324
+ #ifdef HAVE_INIT_EXTENDED_PAIR
1325
+ return (init_extended_pair(NUM2INT(pair), NUM2INT(f), NUM2INT(b)) == OK) ? Qtrue : Qfalse;
1326
+ #else
1324
1327
  return (init_pair(NUM2INT(pair),NUM2INT(f),NUM2INT(b)) == OK) ? Qtrue : Qfalse;
1328
+ #endif
1325
1329
  }
1326
1330
 
1327
1331
  /*
@@ -1345,8 +1349,13 @@ curses_init_color(VALUE obj, VALUE color, VALUE r, VALUE g, VALUE b)
1345
1349
  {
1346
1350
  /* may have to raise exception on ERR */
1347
1351
  curses_stdscr();
1352
+ #ifdef HAVE_INIT_EXTENDED_COLOR
1353
+ return (init_extended_color(NUM2INT(color), NUM2INT(r),
1354
+ NUM2INT(g), NUM2INT(b)) == OK) ? Qtrue : Qfalse;
1355
+ #else
1348
1356
  return (init_color(NUM2INT(color),NUM2INT(r),
1349
1357
  NUM2INT(g),NUM2INT(b)) == OK) ? Qtrue : Qfalse;
1358
+ #endif
1350
1359
  }
1351
1360
 
1352
1361
  /*
@@ -1397,11 +1406,22 @@ curses_colors(VALUE obj)
1397
1406
  static VALUE
1398
1407
  curses_color_content(VALUE obj, VALUE color)
1399
1408
  {
1400
- short r,g,b;
1401
-
1402
1409
  curses_stdscr();
1403
- color_content(NUM2INT(color),&r,&g,&b);
1404
- return rb_ary_new3(3,INT2FIX(r),INT2FIX(g),INT2FIX(b));
1410
+ #ifdef HAVE_EXTENDED_COLOR_CONTENT
1411
+ {
1412
+ int r, g, b;
1413
+ if (extended_color_content(NUM2INT(color), &r, &g, &b) == ERR)
1414
+ return Qnil;
1415
+ return rb_ary_new3(3, INT2FIX(r), INT2FIX(g), INT2FIX(b));
1416
+ }
1417
+ #else
1418
+ {
1419
+ short r, g, b;
1420
+ if (color_content(NUM2INT(color), &r, &g, &b) == ERR)
1421
+ return Qnil;
1422
+ return rb_ary_new3(3, INT2FIX(r), INT2FIX(g), INT2FIX(b));
1423
+ }
1424
+ #endif
1405
1425
  }
1406
1426
 
1407
1427
 
@@ -1430,11 +1450,22 @@ curses_color_pairs(VALUE obj)
1430
1450
  static VALUE
1431
1451
  curses_pair_content(VALUE obj, VALUE pair)
1432
1452
  {
1433
- short f,b;
1434
-
1435
1453
  curses_stdscr();
1436
- pair_content(NUM2INT(pair),&f,&b);
1437
- return rb_ary_new3(2,INT2FIX(f),INT2FIX(b));
1454
+ #ifdef HAVE_EXTENDED_PAIR_CONTENT
1455
+ {
1456
+ int f, b;
1457
+ if (extended_pair_content(NUM2INT(pair), &f, &b) == ERR)
1458
+ return Qnil;
1459
+ return rb_ary_new3(2, INT2FIX(f), INT2FIX(b));
1460
+ }
1461
+ #else
1462
+ {
1463
+ short f, b;
1464
+ if (pair_content(NUM2INT(pair), &f, &b) == ERR)
1465
+ return Qnil;
1466
+ return rb_ary_new3(2, INT2FIX(f), INT2FIX(b));
1467
+ }
1468
+ #endif
1438
1469
  }
1439
1470
 
1440
1471
  /*
@@ -1465,6 +1496,41 @@ curses_pair_number(VALUE obj, VALUE attrs)
1465
1496
  curses_stdscr();
1466
1497
  return INT2FIX(PAIR_NUMBER(NUM2CHTYPE(attrs)));
1467
1498
  }
1499
+
1500
+ /*
1501
+ * Document-method: Curses.support_extended_colors?
1502
+ *
1503
+ * Returns +true+ if the ncurses library was compiled with extended color
1504
+ * support (i.e., init_extended_pair, init_extended_color, etc. are available),
1505
+ * +false+ otherwise.
1506
+ */
1507
+ static VALUE
1508
+ curses_support_extended_colors(VALUE obj)
1509
+ {
1510
+ #if defined(HAVE_INIT_EXTENDED_PAIR) && defined(HAVE_INIT_EXTENDED_COLOR) && \
1511
+ defined(HAVE_EXTENDED_COLOR_CONTENT) && defined(HAVE_EXTENDED_PAIR_CONTENT)
1512
+ return Qtrue;
1513
+ #else
1514
+ return Qfalse;
1515
+ #endif
1516
+ }
1517
+
1518
+ /*
1519
+ * Document-method: Curses.reset_color_pairs
1520
+ *
1521
+ * Resets all color pairs to undefined. Requires ncurses 6.1+.
1522
+ */
1523
+ #ifdef HAVE_RESET_COLOR_PAIRS
1524
+ static VALUE
1525
+ curses_reset_color_pairs(VALUE obj)
1526
+ {
1527
+ curses_stdscr();
1528
+ reset_color_pairs();
1529
+ return Qnil;
1530
+ }
1531
+ #else
1532
+ #define curses_reset_color_pairs rb_f_notimplement
1533
+ #endif
1468
1534
  #endif /* USE_COLOR */
1469
1535
 
1470
1536
  #ifdef USE_MOUSE
@@ -2717,7 +2783,7 @@ window_setscrreg(VALUE obj, VALUE top, VALUE bottom)
2717
2783
  #endif
2718
2784
  }
2719
2785
 
2720
- #if defined(USE_COLOR) && defined(HAVE_WCOLOR_SET)
2786
+ #if defined(USE_COLOR) && ((defined(HAVE_WATTR_SET) && defined(HAVE_WATTR_GET)) || defined(HAVE_WCOLOR_SET))
2721
2787
  /*
2722
2788
  * Document-method: Curses::Window.color_set
2723
2789
  * call-seq: color_set(col)
@@ -2729,13 +2795,26 @@ static VALUE
2729
2795
  window_color_set(VALUE obj, VALUE col)
2730
2796
  {
2731
2797
  struct windata *winp;
2732
- int res;
2733
2798
 
2734
2799
  GetWINDOW(obj, winp);
2735
- res = wcolor_set(winp->window, NUM2INT(col), NULL);
2736
- return (res == OK) ? Qtrue : Qfalse;
2800
+ #if defined(HAVE_WATTR_SET) && defined(HAVE_WATTR_GET)
2801
+ /* Use wattr_set to support pair numbers > 255; preserve existing attrs. */
2802
+ {
2803
+ attr_t attrs;
2804
+ #ifdef NCURSES_PAIRS_T
2805
+ NCURSES_PAIRS_T current_pair;
2806
+ #else
2807
+ short current_pair;
2808
+ #endif
2809
+ if (wattr_get(winp->window, &attrs, &current_pair, NULL) == ERR)
2810
+ return Qfalse;
2811
+ return (wattr_set(winp->window, attrs, NUM2INT(col), NULL) == OK) ? Qtrue : Qfalse;
2812
+ }
2813
+ #else
2814
+ return (wcolor_set(winp->window, NUM2INT(col), NULL) == OK) ? Qtrue : Qfalse;
2815
+ #endif
2737
2816
  }
2738
- #endif /* defined(USE_COLOR) && defined(HAVE_WCOLOR_SET) */
2817
+ #endif /* defined(USE_COLOR) && ((defined(HAVE_WATTR_SET) && defined(HAVE_WATTR_GET)) || defined(HAVE_WCOLOR_SET)) */
2739
2818
 
2740
2819
  /*
2741
2820
  * Document-method: Curses::Window.scroll
@@ -2870,6 +2949,60 @@ window_attrset(VALUE obj, VALUE attrs)
2870
2949
  #endif
2871
2950
  }
2872
2951
 
2952
+ /*
2953
+ * Document-method: Curses::Window.attr_set
2954
+ * call-seq: attr_set(attrs, pair)
2955
+ *
2956
+ * Sets the current attributes and color pair of the given window.
2957
+ * Unlike Curses::Window.attrset, this method accepts an extended color
2958
+ * pair number (> 255) when the ncurses extended colors API is available.
2959
+ *
2960
+ * Returns +true+ on success, +false+ on failure.
2961
+ *
2962
+ * see also system manual curs_attr(3)
2963
+ */
2964
+ #ifdef HAVE_WATTR_SET
2965
+ static VALUE
2966
+ window_attr_set(VALUE obj, VALUE attrs, VALUE pair)
2967
+ {
2968
+ struct windata *winp;
2969
+
2970
+ GetWINDOW(obj, winp);
2971
+ return (wattr_set(winp->window, NUM2UINT(attrs), NUM2INT(pair), NULL) == OK) ? Qtrue : Qfalse;
2972
+ }
2973
+ #endif
2974
+
2975
+ /*
2976
+ * Document-method: Curses::Window.attr_get
2977
+ * call-seq: attr_get => [attrs, pair]
2978
+ *
2979
+ * Returns a 2-element Array of the current attributes and color pair
2980
+ * of the given window. The color pair number may exceed 255 when the
2981
+ * ncurses extended colors API is available.
2982
+ *
2983
+ * Returns +nil+ on failure.
2984
+ *
2985
+ * see also system manual curs_attr(3)
2986
+ */
2987
+ #ifdef HAVE_WATTR_GET
2988
+ static VALUE
2989
+ window_attr_get(VALUE obj)
2990
+ {
2991
+ struct windata *winp;
2992
+ attr_t attrs;
2993
+ #ifdef NCURSES_PAIRS_T
2994
+ NCURSES_PAIRS_T pair;
2995
+ #else
2996
+ short pair;
2997
+ #endif
2998
+
2999
+ GetWINDOW(obj, winp);
3000
+ if (wattr_get(winp->window, &attrs, &pair, NULL) == ERR)
3001
+ return Qnil;
3002
+ return rb_ary_new3(2, UINT2NUM(attrs), INT2NUM(pair));
3003
+ }
3004
+ #endif
3005
+
2873
3006
  /*
2874
3007
  * Document-method: Curses::Window.bkgdset
2875
3008
  * call-seq: bkgdset(ch)
@@ -5071,6 +5204,8 @@ Init_curses(void)
5071
5204
  rb_define_module_function(mCurses, "pair_content", curses_pair_content, 1);
5072
5205
  rb_define_module_function(mCurses, "color_pair", curses_color_pair, 1);
5073
5206
  rb_define_module_function(mCurses, "pair_number", curses_pair_number, 1);
5207
+ rb_define_module_function(mCurses, "support_extended_colors?", curses_support_extended_colors, 0);
5208
+ rb_define_module_function(mCurses, "reset_color_pairs", curses_reset_color_pairs, 0);
5074
5209
  #endif /* USE_COLOR */
5075
5210
  #ifdef USE_MOUSE
5076
5211
  rb_define_module_function(mCurses, "getmouse", curses_getmouse, 0);
@@ -5203,9 +5338,9 @@ Init_curses(void)
5203
5338
  rb_define_method(cWindow, "move", window_move, 2);
5204
5339
  rb_define_method(cWindow, "move_relative", window_move_relative, 2);
5205
5340
  rb_define_method(cWindow, "setpos", window_setpos, 2);
5206
- #if defined(USE_COLOR) && defined(HAVE_WCOLOR_SET)
5341
+ #if defined(USE_COLOR) && ((defined(HAVE_WATTR_SET) && defined(HAVE_WATTR_GET)) || defined(HAVE_WCOLOR_SET))
5207
5342
  rb_define_method(cWindow, "color_set", window_color_set, 1);
5208
- #endif /* USE_COLOR && HAVE_WCOLOR_SET */
5343
+ #endif
5209
5344
  rb_define_method(cWindow, "cury", window_cury, 0);
5210
5345
  rb_define_method(cWindow, "curx", window_curx, 0);
5211
5346
  rb_define_method(cWindow, "maxy", window_maxy, 0);
@@ -5236,6 +5371,12 @@ Init_curses(void)
5236
5371
  rb_define_method(cWindow, "attroff", window_attroff, 1);
5237
5372
  rb_define_method(cWindow, "attron", window_attron, 1);
5238
5373
  rb_define_method(cWindow, "attrset", window_attrset, 1);
5374
+ #ifdef HAVE_WATTR_SET
5375
+ rb_define_method(cWindow, "attr_set", window_attr_set, 2);
5376
+ #endif
5377
+ #ifdef HAVE_WATTR_GET
5378
+ rb_define_method(cWindow, "attr_get", window_attr_get, 0);
5379
+ #endif
5239
5380
  rb_define_method(cWindow, "bkgdset", window_bkgdset, 1);
5240
5381
  rb_define_method(cWindow, "bkgd", window_bkgd, 1);
5241
5382
  rb_define_method(cWindow, "getbkgd", window_getbkgd, 0);
@@ -68,7 +68,7 @@ if $use_bundled_pdcurses
68
68
  $pdcurses_dll_default = true
69
69
  else
70
70
  w64 = $x64 ? "_w64=1" : ""
71
- exec_command "make -f Makefile.mng clean all #{w64} WIDE=Y DLL=N CC=\"gcc -std=gnu17\""
71
+ exec_command "make -f Makefile.mng clean libs #{w64} WIDE=Y DLL=N CC=\"gcc -std=gnu17\""
72
72
  FileUtils.cp("pdcurses.a", File.expand_path("libpdcurses.a", pdcurses_dir))
73
73
  end
74
74
  ensure
@@ -119,7 +119,9 @@ if header_library
119
119
  def_prog_mode reset_prog_mode timeout wtimeout nodelay
120
120
  init_color wcolor_set use_default_colors assume_default_colors
121
121
  newpad unget_wch get_wch wget_wch PDC_get_key_modifiers
122
- chgat wchgat newterm)
122
+ chgat wchgat newterm
123
+ init_extended_color init_extended_pair extended_color_content
124
+ extended_pair_content reset_color_pairs wattr_set wattr_get)
123
125
  have_func(f) || (have_macro(f, curses) && $defs.push(format("-DHAVE_%s", f.upcase)))
124
126
  end
125
127
  convertible_int('chtype', [["#undef MOUSE_MOVED\n"]]+curses) or abort
data/sample/colors.rb CHANGED
@@ -5,6 +5,7 @@ include Curses
5
5
 
6
6
  # The TERM environment variable should be set to xterm-256color etc. to
7
7
  # use 256 colors. Curses.colors returns the color numbers of the terminal.
8
+ # With ncurses 6+ extended color support, color_pairs may exceed 256.
8
9
 
9
10
  begin
10
11
  init_screen
@@ -14,14 +15,24 @@ begin
14
15
  else
15
16
  start_color
16
17
 
17
- addstr "This Terminal supports #{colors} colors.\n"
18
-
19
- Curses.colors.times { |i|
20
- Curses.init_pair(i, i, 0)
21
- attrset(color_pair(i))
18
+ extended = Curses.support_extended_colors?
19
+ addstr "This Terminal supports #{colors} colors, #{color_pairs} pairs"
20
+ addstr extended ? " (extended).\n" : ".\n"
21
+
22
+ (extended ? [512, color_pairs].min : colors).times { |i|
23
+ next if i == 0
24
+ Curses.init_pair(i, i % colors, (i / colors) % colors)
25
+ if extended
26
+ # color_pair() encodes into chtype and can't handle pairs > 255;
27
+ # use color_set on stdscr instead, which calls wattr_set internally.
28
+ stdscr.color_set(i)
29
+ else
30
+ attrset(color_pair(i))
31
+ end
22
32
  addstr("#{i.to_s.rjust(3)} ")
23
33
  addstr("\n") if i == 15 || (i > 16 && (i - 15) % 36 == 0)
24
34
  }
35
+ stdscr.color_set(0)
25
36
  end
26
37
 
27
38
  getch
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curses
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -46,6 +46,9 @@ extensions:
46
46
  - ext/curses/extconf.rb
47
47
  extra_rdoc_files: []
48
48
  files:
49
+ - ".devcontainer/Dockerfile"
50
+ - ".devcontainer/devcontainer.json"
51
+ - ".devcontainer/init-firewall.sh"
49
52
  - ".github/dependabot.yml"
50
53
  - ".github/release.yml"
51
54
  - ".github/workflows/gh-pages.yml"
@@ -345,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
348
  - !ruby/object:Gem::Version
346
349
  version: '0'
347
350
  requirements: []
348
- rubygems_version: 4.0.3
351
+ rubygems_version: 4.0.16
349
352
  specification_version: 4
350
353
  summary: A Ruby binding for curses, ncurses, and PDCurses. curses is an extension
351
354
  library for text UI applications. Formerly part of the Ruby standard library, [curses