com.openfin.desktop.win32.ExternalWindowObserver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openfin-desktop-java-adapter Show documentation
Show all versions of openfin-desktop-java-adapter Show documentation
The Java API for OpenFin Runtime
package com.openfin.desktop.win32;
import com.openfin.desktop.*;
import com.openfin.desktop.EventListener;
import com.openfin.desktop.Window;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import org.json.JSONObject;
import java.awt.*;
import java.net.URISyntaxException;
import java.util.*;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Integrates a window outside of the desktop for interaction and control with the API.
* An integrated window is controlled in the same way as an HTML window running in the desktop.
* It can be controlled and queried by the API,
* generate events that are subscribed to by addEventListener,
* and join/merge groups with other windows for docking.
*
* Created by wche on 3/11/15.
*/
public class ExternalWindowObserver implements DesktopStateListener {
private final static Logger logger = LoggerFactory.getLogger(ExternalWindowObserver.class.getName());
private String appUuid;
private String name;
private DesktopConnection desktopConnection;
private WinDef.HWND hwnd;
private String hwndHex;
private boolean registered = false; // True when this window has been successfully registered with the container.
private AckListener registerListener;
private boolean frameDisabled;
private boolean handlinguserDisabledFrame;
private boolean ignoreNextChange;
private int subscribedSystemEvents;
// Window messges
public static final int WM_CAPTURECHANGED = 0x0215;
public static final int WM_DESTROY = 0x0002;
public static final int WM_ENTERSIZEMOVE = 0x0231;
public static final int WM_ERASEBKGND = 0x0014;
public static final int WM_EXITSIZEMOVE = 0x0232;
public static final int WM_KEYDOWN = 0x0100;
public static final int WM_KEYUP = 0x0101;
public static final int WM_KILLFOCUS = 0x0008;
public static final int WM_LBUTTONDOWN = 0x0201;
public static final int WM_LBUTTONUP = 0x0202;
public static final int WM_MOUSEMOVE = 0x0200;
public static final int WM_MOVE = 0x0003;
public static final int WM_MOVING = 0x0216;
public static final int WM_NCLBUTTONDBLCLK = 0x00A3;
public static final int WM_NCLBUTTONUP = 0x00A2;
public static final int WM_NOTIFY = 0x4E;
public static final int WM_SETFOCUS = 0x0007;
public static final int WM_SIZING = 0x0214;
public static final int WM_SYSCOMMAND = 0x0112;
public static final int WM_WINDOWPOSCHANGED = 0x0047;
public static final int WM_WINDOWPOSCHANGING = 0x0046;
public static final int WM_PAINT = 0x000F;
// System command messages
public static final int SC_CLOSE = 0xF060;
public static final int SC_MAXIMIZE = 0xF030;
public static final int SC_MINIMIZE = 0xF020;
public static final int SC_RESTORE = 0xF120;
// SetWindowPosition
public static final int SWP_HIDEWINDOW = 0x0080;
public static final int SWP_NOMOVE = 0x0002;
public static final int SWP_NOSIZE = 0x0001;
public static final int SWP_SHOWWINDOW = 0x0040;
// Payloads for event propagation to the container
private JSONObject blurPayload;
private JSONObject captureChangedPayload;
private JSONObject destroyedPayload;
private JSONObject enterSizeMovePayload;
private JSONObject exitSizeMovePayload;
private JSONObject focusPayload;
private JSONObject movePayload;
private JSONObject movingPayload;
private JSONObject ncDoubleClickPayload;
private JSONObject posChangedPayload;
private JSONObject posChangingPayload;
private JSONObject sizingPayload;
private JSONObject sysCommandPayload;
/**
* OpenFin window EventListener to listen for FrameDisable events
*/
private EventListener frameDisableListener = new EventListener() {
@Override
public void eventReceived(ActionEvent actionEvent) {
onWindowFrameDisabled();
}
};
/**
* OpenFin window EventListener to listen for FrameEnable events
*/
private EventListener frameEnableListener = new EventListener() {
@Override
public void eventReceived(ActionEvent actionEvent) {
onWindowFrameEnabled();
}
};
/**
*
* Establishes a connection and registers
* the window identified by hWnd with the Runtime.
*
* @param port The host that the desktop is running on
* @param parentAppUuid The UUID of the application to create register this window as a child of
* @param name The unique name for this window as a child window
* @param window The HWND of the window to control/observe
* @param listener AckListener for external registering
* @throws DesktopException if registration fails
*/
public ExternalWindowObserver(int port, String parentAppUuid, String name, java.awt.Window window, AckListener listener) throws DesktopException {
logger.debug("init for " + name);
this.appUuid = parentAppUuid;
this.name = name;
this.desktopConnection = new DesktopConnection(UUID.randomUUID().toString(), null, port);
this.hwnd = new WinDef.HWND();
this.hwnd.setPointer(Native.getWindowPointer(window));
this.hwndHex = Long.toHexString(Native.getWindowID(window));
this.registerListener = listener;
this.subscribedSystemEvents = (SC_CLOSE | SC_MAXIMIZE | SC_MINIMIZE | SC_RESTORE);
this.desktopConnection.connect(this);
}
/**
* Get UUID of parent application
* @return UUID
*/
public String getAppUuid() {
return this.appUuid;
}
/**
* Get name of this window
* @return name
*/
public String getName() {
return this.name;
}
/**
* deregisters the window with the desktop
*
* @throws DesktopException if errors are caught during cleanup
*/
private void cleanup() throws DesktopException{
logger.debug("cleaning up");
try {
final CountDownLatch deregisterLatch = new CountDownLatch(1);
// Notify the desktop to remove this external window
deregisterExternalWindow(new AckListener() {
public void onSuccess(Ack ack) {
deregisterLatch.countDown();
}
public void onError(Ack ack) {
logger.error(String.format("Error registering external window %s", ack.getReason()));
deregisterLatch.countDown();
}
});
deregisterLatch.await(5, TimeUnit.SECONDS);
// Reset the normal WndProc
WinMessageHelper.unhookWndProc(this.hwnd);
// Ensure the deregister gets sent.
this.desktopConnection.disconnect();
} catch (Exception ex) {
logger.info("Error cleaning up", ex);
}
}
/**
* Ensures this window is deregistered on disposal
*
* @throws DesktopException if errors are caught during cleanup
*/
public void dispose() throws DesktopException{
cleanup();
}
/**
*
* Triggered when a "frame-enabled" event is triggered for the registered external window.
*
*/
private void onWindowFrameEnabled() {
logger.debug("window frame enabled " + name);
this.frameDisabled = false;
}
/**
*
* Triggered when a "frame-disabled" event is triggered for the registered external window.
*
*/
private void onWindowFrameDisabled() {
logger.debug("window frame disabled " + name);
this.frameDisabled = true;
}
/**
*
* Notifies the desktop to track, control and observe events for this window
*
* @param callback AckListener for the request
*/
private void registerExternalWindow(final AckListener callback) {
logger.debug("registering " + name);
JSONObject registerExternalWindowPayload = new JSONObject();
registerExternalWindowPayload.put("topic", "application");
registerExternalWindowPayload.put("uuid", this.appUuid);
registerExternalWindowPayload.put("hwnd", this.hwndHex);
registerExternalWindowPayload.put("name", this.name);
AckListener subscribeToFrameSateChange = new AckListener() {
@Override
public void onSuccess(final Ack ack) {
// this is on websocket io thread, need to run on another thread
Thread thread = new Thread() {
public void run() {
// Subscribe to frame state change events
try {
Window wnd = Window.wrap(appUuid, name, desktopConnection);
DesktopUtils.addEventListener(wnd, "frame-disabled", frameDisableListener);
DesktopUtils.addEventListener(wnd, "frame-enabled", frameEnableListener);
DesktopUtils.successAck(callback, ack);
} catch (Exception ex) {
logger.error("Error registering external window", ex);
DesktopUtils.errorAckOnException(callback, ExternalWindowObserver.this, ex);
}
}
};
thread.setName(ExternalWindowObserver.class.getName() + ".registerExternalWindow");
thread.start();
}
@Override
public void onError(Ack ack) {
logger.warn("Error registering external window " + ack.getReason());
DesktopUtils.errorAck(callback, ack);
}
};
this.desktopConnection.sendAction("register-external-window", registerExternalWindowPayload, subscribeToFrameSateChange, this);
}
/**
* Notifies the desktop to stop all integration with this window, and remove it from the app
*
* @param callback AckListener for this request
* @see com.openfin.desktop.AckListener
*
*/
private void deregisterExternalWindow(final AckListener callback) {
if (this.registered) {
logger.debug("deregistering " + name);
this.registered = false;
JSONObject deregisterExternalWindowPayload = new JSONObject();
deregisterExternalWindowPayload.put("topic", "application");
deregisterExternalWindowPayload.put("uuid", appUuid);
deregisterExternalWindowPayload.put("hwnd", hwndHex);
deregisterExternalWindowPayload.put("name", name);
AckListener deSubscribeToFrameSateChange = new AckListener() {
@Override
public void onSuccess(Ack ack) {
// Subscribe to frame state change events
Window wnd = Window.wrap(appUuid, name, desktopConnection);
wnd.removeEventListener("frame-disabled", frameDisableListener, null);
wnd.removeEventListener("frame-enabled", frameEnableListener, null);
DesktopUtils.successAck(callback, ack);
}
@Override
public void onError(Ack ack) {
logger.warn("Error deregistering external window " + ack.getReason());
DesktopUtils.errorAck(callback, ack);
}
};
desktopConnection.sendAction("deregister-external-window", deregisterExternalWindowPayload, deSubscribeToFrameSateChange, this);
}
}
/**
* sendExternalWindowEvent
*
* @param payload The serialized WM message
*
*/
private void sendExternalWindowEvent(JSONObject payload) {
if (this.registered) {
if (logger.isDebugEnabled()) {
logger.debug(payload.toString());
}
desktopConnection.sendAction("external-window-action", payload, null, this);
}
}
/**
*
* Install delegates to send WM events to the desktop
*
*/
private void installMessageHandlers() {
WindowProcCallback windowProcCallback = new WindowProcCallback() {
@Override
public boolean callback(WinDef.HWND hwnd, int msg, WinDef.WPARAM wp, Pointer lp) {
boolean handled = false;
try {
switch (msg) {
case WM_CAPTURECHANGED:
onCaptureChanged(hwnd, msg, wp, lp);
break;
case WM_DESTROY:
onWMDestroy(msg);
break;
case WM_ENTERSIZEMOVE:
onEnterSizeMove(msg);
break;
case WM_EXITSIZEMOVE:
onExitSizeMove(msg);
break;
case WM_KILLFOCUS:
onKillFocus(msg);
break;
case WM_MOVE:
onMove(msg, wp, lp);
break;
case WM_MOVING:
handled = onMoving(msg, wp, lp);
break;
case WM_NCLBUTTONDBLCLK:
onNcDoubleClick(msg, wp, lp);
break;
case WM_SETFOCUS:
onSetFocus(msg);
break;
case WM_SYSCOMMAND:
onSysCommand(msg, wp);
break;
case WM_WINDOWPOSCHANGED:
onWindowPosChanged(msg, wp, lp);
break;
case WM_WINDOWPOSCHANGING:
onWindowPosChanging(msg, wp, lp);
break;
case WM_SIZING:
handled = onSizing(msg, wp, lp);
break;
}
} catch (Exception e) {
logger.error("Error processing callback", e);
}
return handled;
}
};
try {
WinMessageHelper.hookWndProc(hwnd, windowProcCallback);
if (this.registerListener != null) {
JSONObject msg = new JSONObject();
msg.put("success", Boolean.TRUE);
DesktopUtils.successAck(this.registerListener, new Ack(msg, this));
}
} catch (Throwable e) {
JSONObject msg = new JSONObject();
msg.put("success", Boolean.FALSE);
DesktopUtils.errorAck(this.registerListener, new Ack(msg, this));
logger.error("Error initializing", e);
}
}
/**
* Called in response to WM_CAPTURECHANGED
*
* @param hwnd A handle to the window
* @param msg WM_CAPTURECHANGED
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private void onCaptureChanged(WinDef.HWND hwnd, int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onCaptureChanged");
if (this.captureChangedPayload == null) {
this.captureChangedPayload = createBaseWMJSONObject(msg);
}
WinDef.HWND h = new WinDef.HWND(lp);
this.captureChangedPayload.put( "lParam", Pointer.nativeValue(lp));
sendExternalWindowEvent(this.captureChangedPayload);
if (handlinguserDisabledFrame && !hwnd.equals(h)) {
ignoreNextChange = true;
}
}
/**
* Called in response to WM_DESTROY
*
* @param msg WM_DESTROY
*/
private void onWMDestroy(int msg) {
try {
logger.debug("onWMDestroy");
sendExternalWindowEvent(createBaseWMJSONObject(msg));
cleanup();
} catch (DesktopException e) {
logger.error("Error onWMDestroy", e);
}
}
/**
* Called in response to WM_ENTERSIZEMOVE
*
* @param msg WM_ENTERSIZEMOVE
*/
private void onEnterSizeMove(int msg) {
logger.debug("onEnterSizeMove");
if (!handlinguserDisabledFrame && frameDisabled) {
logger.debug("set handlinguserDisabledFrame true");
handlinguserDisabledFrame = true;
}
if (enterSizeMovePayload == null) {
enterSizeMovePayload = createBaseWMJSONObject(msg);
}
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
enterSizeMovePayload.put("mouseX", b.getX());
enterSizeMovePayload.put("mouseY", b.getY());
sendExternalWindowEvent(enterSizeMovePayload);
}
/**
* Called in response to WM_EXITSIZEMOVE
*
* @param msg WM_EXITSIZEMOVE
*/
private void onExitSizeMove(int msg) {
logger.debug("onExitSizeMove");
if (handlinguserDisabledFrame) {
logger.debug("set handlinguserDisabledFrame false");
handlinguserDisabledFrame = false;
}
if (exitSizeMovePayload == null) {
exitSizeMovePayload = createBaseWMJSONObject(msg);
}
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
exitSizeMovePayload.put("mouseX", b.getX());
exitSizeMovePayload.put("mouseY", b.getY());
sendExternalWindowEvent(exitSizeMovePayload);
}
/**
* Called in response to WM_KILLFOCUS
*
* @param msg WM_KILLFOCUS
*/
private void onKillFocus(int msg) {
logger.debug("onKillFocus");
sendExternalWindowEvent(createBaseWMJSONObject(msg));
}
/**
* Called in response to WM_MOVE
*
* @param msg WM_MOVE
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private void onMove(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onMove ");
if (this.movePayload == null) {
this.movePayload = createBaseWMJSONObject(msg);
}
// numbers from lp are for client area. They are NOT window location
// WinDef.DWORD dword = new WinDef.DWORD(Pointer.nativeValue(lp));
// logger.fine("onMove " + dword.getLow().intValue() + ":" + dword.getHigh().intValue());
WinDef.RECT bounds = new WinDef.RECT();
User32.INSTANCE.GetWindowRect(this.hwnd, bounds);
movePayload.put("x", bounds.left);
movePayload.put("y", bounds.top);
sendExternalWindowEvent(movePayload);
}
/**
* Called in response to WM_MOVING
*
* @param msg WM_MOVING
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private boolean onMoving(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onMoving");
boolean handled = false;
if (movingPayload == null) {
this.movingPayload = createBaseWMJSONObject(msg);
}
WINDOWRECT movingBounds = new WINDOWRECT(lp);
movingPayload.put("left", movingBounds.left);
movingPayload.put("top", movingBounds.top);
movingPayload.put("right", movingBounds.right);
movingPayload.put("bottom", movingBounds.bottom);
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
movingPayload.put("mouseX", b.getX());
movingPayload.put("mouseY", b.getY());
if (handlinguserDisabledFrame) {
logger.debug("handling disabled frame");
//Retrieve the current bounds
WinDef.RECT currentBounds = new WinDef.RECT();
User32.INSTANCE.GetWindowRect(this.hwnd, currentBounds);
structureToPtr(currentBounds, lp);
handled = true;
}
sendExternalWindowEvent(movingPayload);
return handled;
}
/**
* Called in response to WM_NCLBUTTONDBLCLK
*
* @param msg WM_NCLBUTTONDBLCLK
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private void onNcDoubleClick(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onNcDoubleClick");
if (ncDoubleClickPayload == null) {
this.ncDoubleClickPayload = createBaseWMJSONObject(msg);
}
ncDoubleClickPayload.put("wParam", wp.intValue());
// getting data from lp throws exception sometimes
// WinDef.POINT point = new WinDef.POINT(lp);
// ncDoubleClickPayload.put("x", point.x);
// ncDoubleClickPayload.put("y", point.y);
WinDef.POINT mouse = new WinDef.POINT();
WinMessageHelper.customUser32.GetCursorPos(mouse);
ncDoubleClickPayload.put("x", mouse.x);
ncDoubleClickPayload.put("y", mouse.y);
sendExternalWindowEvent(ncDoubleClickPayload);
}
/**
* Called in response to WM_SETFOCUS
*
* @param msg WM_SETFOCUS
*
*/
private void onSetFocus(int msg) {
logger.debug("onSetFocus");
if (this.focusPayload == null) {
this.focusPayload = createBaseWMJSONObject(msg);
}
sendExternalWindowEvent(focusPayload);
}
/**
* Called in response to WM_SYSCOMMAND
*
* @param msg WM_SYSCOMMAND
*
*/
private void onSysCommand(int msg, WinDef.WPARAM wp) {
logger.debug("onSysCommand");
if (sysCommandPayload == null) {
this.sysCommandPayload = createBaseWMJSONObject(msg);
}
// Only send events we are interested in
if ((wp.intValue() & subscribedSystemEvents) > 0) {
sysCommandPayload.put("wParam", wp.intValue());
sendExternalWindowEvent(sysCommandPayload);
}
}
/**
* Called in response to WM_WINDOWPOSCHANGED
*
* @param msg WM_WINDOWPOSCHANGED
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private void onWindowPosChanged(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onWindowPosChanged ");
if (posChangedPayload == null) {
this.posChangedPayload = createBaseWMJSONObject(msg);
}
WINDOWPOS changeInfo = new WINDOWPOS(lp);
posChangedPayload.put("hwnd", toHWNDHex(changeInfo.hwnd));
if (changeInfo.hwndInsertAfter != null) {
posChangedPayload.put("hwndInsertAfter", toHWNDHex(changeInfo.hwndInsertAfter));
}
posChangedPayload.put("x", changeInfo.x);
posChangedPayload.put("y", changeInfo.y);
posChangedPayload.put("cx", changeInfo.cx);
posChangedPayload.put("cy", changeInfo.cy);
posChangedPayload.put("flags", changeInfo.flags);
sendExternalWindowEvent(posChangedPayload);
}
/**
* Called in response to WM_WINDOWPOSCHANGING
*
* @param msg WM_WINDOWPOSCHANGING
* @param wp wParam of Windows message callback
* @param lp lParam of Windows message callback
*/
private void onWindowPosChanging(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onWindowPosChanging ");
if (this.posChangingPayload == null) {
this.posChangingPayload = createBaseWMJSONObject(msg);
}
WINDOWPOS changeInfo = new WINDOWPOS(lp);
posChangingPayload.put("hwnd", toHWNDHex(changeInfo.hwnd));
if (changeInfo.hwndInsertAfter != null) {
posChangingPayload.put("hwndInsertAfter", toHWNDHex(changeInfo.hwndInsertAfter));
}
posChangingPayload.put("x", changeInfo.x);
posChangingPayload.put("y", changeInfo.y);
posChangingPayload.put("cx", changeInfo.cx);
posChangingPayload.put("cy", changeInfo.cy);
posChangingPayload.put("flags", changeInfo.flags);
// Moving allowed
boolean isMoving = (changeInfo.flags & SWP_NOMOVE) == 0;
// Sizing allowed
boolean isSizing = (changeInfo.flags & SWP_NOSIZE) == 0;
// Delegate to desktop if this is a move/size and handling a disabled user frame
if ((isMoving || isSizing)) {
if (handlinguserDisabledFrame && ignoreNextChange) {
logger.debug("prevent pos changing");
changeInfo.flags |= (SWP_NOMOVE | SWP_NOSIZE);
// Copy the flag change to lParam unmanaged memory.
// Prevents changes
structureToPtr(changeInfo, lp);
}
ignoreNextChange = false;
}
sendExternalWindowEvent(posChangingPayload);
}
/**
* Called in response to WM_SIZING
*
* @param msg WM_SIZING
* @param wp wParam of Windows message callback
*/
private boolean onSizing(int msg, WinDef.WPARAM wp, Pointer lp) {
logger.debug("onSizing");
boolean handled = false;
if (sizingPayload == null) {
sizingPayload = createBaseWMJSONObject(msg);
}
sizingPayload.put("wParam", wp.intValue());
WINDOWRECT changeInfo = new WINDOWRECT(lp);
sizingPayload.put("left", changeInfo.left);
sizingPayload.put("top", changeInfo.top);
sizingPayload.put("right", changeInfo.right);
sizingPayload.put("bottom", changeInfo.bottom);
WinDef.POINT mouse = new WinDef.POINT();
WinMessageHelper.customUser32.GetCursorPos(mouse);
sizingPayload.put("mouseX", mouse.x);
sizingPayload.put("mouseY", mouse.y);
if (handlinguserDisabledFrame) {
//Retrieve the current bounds
WinDef.RECT currentBounds = new WinDef.RECT();
User32.INSTANCE.GetWindowRect(this.hwnd, currentBounds);
// Copy the current bounds struct to lParam unmanaged memory.
// Prevents changes
structureToPtr(currentBounds, lp);
handled = true;
}
sendExternalWindowEvent(sizingPayload);
return handled;
}
/**
* Create base JSONObject for all message types
* @param msg message type
* @return JSONObject
*/
private JSONObject createBaseWMJSONObject(int msg) {
JSONObject payload = new JSONObject();
payload.put("uuid", appUuid);
payload.put("name", name);
payload.put("type", msg);
return payload;
}
/**
* Convert HWND to hex
* @param hwnd HWND
* @return hex string
*/
private String toHWNDHex(WinDef.HWND hwnd) {
return Long.toHexString(Pointer.nativeValue(hwnd.getPointer()));
}
private void structureToPtr(Structure struture, Pointer pointer) {
byte[] buffer = new byte[struture.size()];
struture.getPointer().read(0, buffer, 0, struture.size());
pointer.write(0, buffer, 0, struture.size());
}
/**
* DesktopStateListener.onReady
*/
@Override
public void onReady() {
registerExternalWindow(new AckListener() {
@Override
public void onSuccess(Ack ack) {
registered = true;
installMessageHandlers();
}
@Override
public void onError(Ack ack) {
DesktopUtils.errorAck(registerListener, ack);
}
});
}
/**
* DesktopStateListener.onClose
*
*/
@Override
public void onClose(String error) {
}
/**
* DesktopStateListener.onError
*
* @param reason Error message
*/
@Override
public void onError(String reason) {
}
/**
* DesktopStateListener.onMessage
*
* @param message Message text
*/
@Override
public void onMessage(String message) {
}
/**
* DesktopStateListener.onOutgoingMessage
*
* @param message Message text
*/
@Override
public void onOutgoingMessage(String message) {
}
/**
* Structure of WindowPos
*/
public static class WINDOWPOS extends Structure {
public WinDef.HWND hwnd;
public WinDef.HWND hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
public WINDOWPOS(Pointer memory) {
super(memory);
read();
}
@Override
protected List getFieldOrder() {
String[] aa = {"hwnd", "hwndInsertAfter", "x", "y", "cx", "cy", "flags"};
return Arrays.asList(aa);
}
}
public static class WINDOWRECT extends Structure {
public int left;
public int top;
public int right;
public int bottom;
public WINDOWRECT(Pointer memory) {
super(memory);
read();
}
protected List getFieldOrder() {
return Arrays.asList(new String[] { "left", "top", "right", "bottom" });
}
public Rectangle toRectangle() {
return new Rectangle(left, top, right - left, bottom - top);
}
public String toString() {
return "[(" + left + "," + top + ")(" + right + "," + bottom + ")]";
}
}
}