惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Security Latest
Security Latest
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
S
Securelist
Google DeepMind News
Google DeepMind News
S
Schneier on Security
T
Troy Hunt's Blog
Help Net Security
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
O
OpenAI News
博客园 - Franky
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy International News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Google Online Security Blog
Google Online Security Blog
Latest news
Latest news
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
博客园_首页
S
Security Affairs
PCI Perspectives
PCI Perspectives
WordPress大学
WordPress大学
C
Cisco Blogs
Recent Announcements
Recent Announcements
L
LangChain Blog
GbyAI
GbyAI
F
Fortinet All Blogs
N
News and Events Feed by Topic
T
Tor Project blog
IT之家
IT之家
P
Palo Alto Networks Blog
D
DataBreaches.Net
小众软件
小众软件
宝玉的分享
宝玉的分享
F
Full Disclosure

博客园 - SHARP-EYE

Processing在P2D模式下使用 tint() 方法的注意事项 · 只对 image() 和纹理有效 Processing 的 sketch.properties 机制以及主程序与文件夹解耦的具体细节 Kimi Code CLI 系统指令的摸索 以及 开发实战经验分享 开源重置 沙丘2 游戏 · Dune Legacy 中文本地化与渲染技术说明 基于 Irrlicht 和 WASAPI 的 Simple Audio Visualization 技术开发报告 AI Coding越来越强,我们还有必要学Processing吗? · 创意编程 嵌入式脚本语言全解析:从Lua到Wren,游戏与IoT开发的未来选型指南 PixelMentor:一个开源网站 · 调用AI视觉能力分析图片 · 提供影视后期修改意见 基于 Vanilla JS 构建高性能可视化节点创意展示编辑器 (CNE) 的技术实践与深度解析 · Creative Node Editor 关于OpenClaw部署在Ubuntu上的经验小结 P3DE (Processing 3D Editor) 三维场景编辑器 · 软件白皮书 · 基于 v0.4.8 Windows 批量文件操作风险指南 · 不完整小结 [深度复盘] 构建高性能实时弹幕系统:Node.js + Socket.io 架构设计与生产环境部署实战 如何调用CMD实现多个同类文件合并的研究 · 二进制 · 依次 · 文本图像视频音频 Processing (Java) 中实现2D任意图形的鼠标悬停检测 · 2D射线检测 · 模拟按钮 · 点击事件 PowerShell实现全屏七彩渐变 · 呼吸 · 屏保 浅谈processing-java.exe应用程序的使用(与PowerShell的联合) 使用Windows任务计划程序实现每天更换一张Processing创意桌面壁纸 PowerShell开发小工具 · 四张照片拼成一张 CMD批处理脚本+VBScript脚本+Potplayer 实现文件夹内所有视频的截图任务(指定时间点) 批处理脚本(.bat)实现实时监测文件夹并执行命令 [假设有新文件则拷贝到远程文件夹内] PowerShell开发游戏 · 打蜜蜂 Powershell实现圆缩小放大 (实时刷新窗口)
Processing P2D 模式鼠标约束 · 限制在窗口内 · 技术报告
SHARP-EYE · 2026-04-25 · via 博客园 - SHARP-EYE

Processing P2D 模式鼠标约束技术报告

概述

在 Processing 的 P2D(以及 P3D)渲染器模式下,由于底层使用 JOGL(Java OpenGL)库实现,约束鼠标在窗口内的方法与标准的 JAVA2D 渲染器有所不同。本报告详细阐述两种主要的鼠标约束方法及其适用场景。


方法一:物理约束(GLWindow.confinePointer)

原理

P2D 渲染器底层使用 JOGL 的 GLWindow 对象来管理窗口和输入。通过 surface.getNative() 方法可以获取这个原生窗口对象,然后调用 confinePointer(true) 将鼠标物理限制在窗口边界内。

实现代码

import com.jogamp.newt.opengl.GLWindow;

void setup() {
  size(640, 480, P2D);

  GLWindow window = (GLWindow) surface.getNative();
  window.confinePointer(true);
}

核心 API

方法 说明
confinePointer(boolean) 启用/禁用鼠标指针约束。true=限制在窗口内
setPointerVisible(boolean) 显示/隐藏鼠标指针
warpPointer(int x, int y) 将鼠标指针移动到指定窗口坐标

特点

  • 优点:鼠标被真正锁定在窗口内,无法移出,适合 FPS 类游戏或需要捕捉鼠标的应用
  • 缺点:用户体验上鼠标不能移出窗口,可能造成不便
  • 返回值mouseXmouseY 无需额外处理,天然就在窗口范围内

高级用法:FPS 风格鼠标控制

import com.jogamp.newt.opengl.GLWindow;

float rotX = 0, rotY = 0;
float sensitivity = 0.01;

void setup() {
  size(640, 480, P2D);
  GLWindow window = (GLWindow) surface.getNative();
  window.confinePointer(true);
  window.setPointerVisible(false);
}

void draw() {
  background(40);

  // 计算鼠标移动增量(从中心计算)
  float dx = mouseX - width / 2;
  float dy = mouseY - height / 2;

  rotY += dx * sensitivity;
  rotX += dy * sensitivity;

  // 将鼠标重置到中心
  GLWindow window = (GLWindow) surface.getNative();
  window.warpPointer(width / 2, height / 2);

  // 使用旋转值绘制场景
  pushMatrix();
  translate(width / 2, height / 2);
  rotateX(rotX);
  rotateY(rotY);
  box(100);
  popMatrix();
}

方法二:数值约束(constrain 函数)

原理

使用 Processing 内置的 constrain() 函数,确保 mouseXmouseY 的值被限制在指定范围内。这种方法不会物理限制鼠标,但可以确保程序逻辑中使用的坐标值始终在有效范围内。

实现代码

void draw() {
  float cx = constrain(mouseX, 0, width);
  float cy = constrain(mouseY, 0, height);

  ellipse(cx, cy, 20, 20);
}

特点

  • 优点:实现简单,不影响用户正常鼠标操作
  • 缺点:鼠标实际可以移出窗口,当鼠标在窗口外时 mouseX/mouseY 会是负值或超出范围
  • 适用场景:UI 元素拖拽、画布绘制、任何需要安全坐标值的场景

完整示例:带边界检测的绘制

float brushX, brushY;
int brushSize = 20;

void setup() {
  size(640, 480, P2D);
}

void draw() {
  background(255);

  // 约束画笔位置
  brushX = constrain(mouseX, brushSize/2, width - brushSize/2);
  brushY = constrain(mouseY, brushSize/2, height - brushSize/2);

  // 绘制画笔预览
  noFill();
  stroke(200);
  ellipse(brushX, brushY, brushSize, brushSize);

  // 绘制信息
  fill(0);
  text("brushX: " + brushX, 10, 20);
  text("brushY: " + brushY, 10, 35);
}

方法三:JAVA2D 渲染器的 Robot 方法

对于使用 JAVA2D 渲染器的 sketch,需要使用 java.awt.Robot 类:

import java.awt.Robot;
import java.awt.Point;

Robot robot;

void setup() {
  size(640, 480, JAVA2D);
  robot = new Robot();
}

void mouseMoved() {
  // 获取窗口屏幕坐标
  Point p = ((PSurfaceAWT)surface).getFrame().getLocationOnScreen();
  int centerX = p.x + width / 2;
  int centerY = p.y + height / 2;

  // 将鼠标重置到窗口中心
  robot.mouseMove(centerX, centerY);
}

注意:JAVA2D 模式下 surface.getNative() 返回的是 AWT Frame,不是 GLWindow。


不同渲染器对比

特性 P2D / P3D JAVA2D
底层技术 JOGL (NEWT) AWT
获取原生窗口 surface.getNative() → GLWindow ((PSurfaceAWT)surface).getFrame()
指针约束 GLWindow.confinePointer() java.awt.Robot
指针隐藏 GLWindow.setPointerVisible() java.awt.Robot + 自定义光标
窗口坐标 原生支持 需要计算偏移

常见问题与解决方案

Q1: 为什么 mouseX/mouseY 会有负值?

当鼠标移出窗口时,P2D 模式下 mouseX/mouseY 会变成负值。解决方案:

void draw() {
  float safeX = constrain(mouseX, 0, width);
  float safeY = constrain(mouseY, 0, height);
  // 使用 safeX, safeY 代替 mouseX, mouseY
}

Q2: 如何检测鼠标是否在窗口内?

boolean isMouseInside() {
  return mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height;
}

Q3: confinePointer(true) 没有生效?

确保:

  1. setup() 中调用(不是在 draw() 中)
  2. 已经正确导入:import com.jogamp.newt.opengl.GLWindow;
  3. 使用的是 P2D 或 P3D 渲染器

Q4: 鼠标重置到中心时出现抖动?

这是正常的,因为 warpPointermouseMoved 事件之间存在竞态条件。可以使用标志位跳过第一次重置:

boolean initialized = false;

void mouseMoved() {
  if (initialized) {
    // 重置到中心
    GLWindow window = (GLWindow) surface.getNative();
    window.warpPointer(width / 2, height / 2);
  }
  initialized = true;
}

最佳实践

  1. 游戏类应用:使用 GLWindow.confinePointer(true) 配合 warpPointer() 实现 FPS 风格控制

  2. 交互式绘画/设计工具:使用 constrain() 函数确保绘制内容始终在画布内

  3. 需要鼠标按下拖拽:先 confinePointer(true) 再隐藏指针,提供沉浸式体验

  4. 跨渲染器兼容:封装成工具类,根据渲染器类型选择正确的方法

void constrainMouse(boolean enable) {
  if (enable) {
    if (useGL) {
      GLWindow window = (GLWindow) surface.getNative();
      window.confinePointer(true);
    } else {
      // JAVA2D 降级处理
    }
  }
}

参考文献


参考程序

/**
 * Processing P2D 模式 - 鼠标约束示例
 *
 * 演示两种约束鼠标的方法:
 * 1. 物理约束:真正限制鼠标在窗口内移动
 * 2. 数值约束:仅限制 mouseX/mouseY 的返回值
 */

// 方法一:使用 GLWindow 物理约束鼠标
import com.jogamp.newt.opengl.GLWindow;

boolean usePhysicalConstraint = true;  // 切换两种模式

void setup() {
  size(640, 480, P2D);

  // 方法一:使用 GLWindow 限制鼠标在窗口内
  if (usePhysicalConstraint) {
    GLWindow window = (GLWindow) surface.getNative();
    window.confinePointer(true);
    // window.setPointerVisible(false);  // 如果需要可以隐藏指针
  }
}

void draw() {
  background(40);

  // 绘制窗口边界
  stroke(100);
  noFill();
  rect(0, 0, width-1, height-1);

  // 绘制鼠标位置指示
  float displayX, displayY;

  if (usePhysicalConstraint) {
    // 方法一:物理约束 - mouseX/mouseY 天然就在窗口内
    displayX = mouseX;
    displayY = mouseY;
    fill(0, 255, 0);
    text("模式: 物理约束 (GLWindow.confinePointer)", 10, 25);
  } else {
    // 方法二:数值约束 - 确保值在窗口范围内
    displayX = constrain(mouseX, 0, width);
    displayY = constrain(mouseY, 0, height);
    fill(255, 200, 0);
    text("模式: 数值约束 (constrain 函数)", 10, 25);
  }

  // 绘制十字准星
  stroke(255);
  strokeWeight(1);
  line(displayX - 15, displayY, displayX + 15, displayY);
  line(displayX, displayY - 15, displayX, displayY + 15);

  // 绘制圆形
  noStroke();
  fill(255, 100);
  ellipse(displayX, displayY, 30, 30);

  // 显示鼠标坐标
  fill(255);
  textSize(12);
  text("mouseX: " + displayX + "  mouseY: " + displayY, 10, height - 15);
}

// 按空格键切换模式
void keyPressed() {
  if (key == ' ') {
    usePhysicalConstraint = !usePhysicalConstraint;
    // 注意:物理约束需要在 setup 中设置,这里只是演示切换提示
    if (!usePhysicalConstraint) {
      GLWindow window = (GLWindow) surface.getNative();
      window.confinePointer(false);
    } else {
      GLWindow window = (GLWindow) surface.getNative();
      window.confinePointer(true);
    }
  }
}