I want to grab the all global key events on linux using JNA same as window hooks. following is the code:
package pack.tracker.services;
//import sun.awt.X11.XEvent;
//import com.jscape.inet.ssh.transport.KeyCreator;
import com.sun.jna.examples.unix.X11;
import com.sun.jna.examples.unix.X11.Display;
import com.sun.jna.examples.unix.X11.Window;
import com.sun.jna.examples.unix.X11.XEvent;
public class JNATest {
X11 x = X11.INSTANCE;
Window window;
Display display = x.XOpenDisplay("0");
XEvent ev = new XEvent();
public JNATest() {
System.out.println("hi");
// TODO Auto-generated constructor stub
String keyString = "F3" ;
Display display = x.XOpenDisplay(null);
XEvent ev = new XEvent();
window = x.XDefaultRootWindow(display);
for(;;){
x.XNextEvent(display, ev);
if(ev.type == X11.KeyPress){
System.out.println("KeyPressed");
}
else{
System.out.println("ok");
}
}
}
public static void main(String s[]){
new JNATest();
}
}
I donot know whats going wrong. I am unable to use XGrabKeyboard. Please help me out.
Thanks and regards,
Vivek Birdi
The XGrabKeys method has not been implemented in JNA. You will need to extend the X11 library and define the method like so:
import com.sun.jna.*;
import com.sun.jna.platform.unix.X11;
import com.sun.jna.platform.unix.X11.*;
public interface Xlib extends X11 {
int XGrabKey(Display display, int keycode, NativeLong modifiers, Window grab_window, boolean owner_events, int pointer_mode, int keyboard_mode);
/*
Display *display;
int keycode;
unsigned int modifiers;
Window grab_window;
Bool owner_events;
int pointer_mode, keyboard_mode;
*/
}
Have a look at this site for Xlib structures and methods.
If you are still unable to implement in JNA then have a look at JXGrabkey at(http://sourceforge.net/projects/jxgrabkey/).
Related
Hello people of the internet, I would like to know if there was a way to make a custom food item give you XP. I am in the middle of making a mod and would like "SimonApple" To give me XP. Please let me know if there is a way to do this.
package com.notsimon.blocksplus;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemFood;
import net.minecraft.item.ItemStack;
#Mod(modid = "ep", name = "Experience Plus", version = "1.0")
public class ExperiencePlus {
public static Item SimonApple;
public static Item MagentaDust;
public static Block MagentaOre;
public static Block MagentaBlock;
#EventHandler
public void preInit(FMLPreInitializationEvent event) {
//Item/Block init and registering
//Config handling
//X * 0.5 = 20 Hunger
SimonApple = new ItemFood(10, 0.5F, false).setUnlocalizedName("SimonApple").setTextureName("bp:SimonApple").setCreativeTab(tabBlocksPlus);
MagentaOre = new BlockTable(Material.iron).setBlockName("MagentaOre").setBlockTextureName("bp:MagentaOre").setCreativeTab(tabBlocksPlus);
MagentaDust = new ItemTable().setUnlocalizedName("MagentaDust").setTextureName("bp:MagentaDust").setCreativeTab(tabBlocksPlus);
MagentaBlock = new MagentaBlock(Material.iron).setBlockName("MagentaBlock").setBlockTextureName("bp:MagentaBlock").setCreativeTab(tabBlocksPlus);
//item.itemTable and substring(5) removes "item."
GameRegistry.registerItem(SimonApple, SimonApple.getUnlocalizedName().substring(5));
GameRegistry.registerBlock(MagentaOre, MagentaOre.getUnlocalizedName().substring(5));
GameRegistry.registerItem(MagentaDust, MagentaDust.getUnlocalizedName().substring(5));
GameRegistry.registerBlock(MagentaBlock, MagentaBlock.getUnlocalizedName().substring(5));
GameRegistry.registerWorldGenerator(new OreGeneration(), 0);
}
#EventHandler
public void init(FMLInitializationEvent event) {
//Proxy, TileEntity, entity, GUI and Packet Registering
GameRegistry.addRecipe(new ItemStack(SimonApple, 2), new Object[]{"MMM","MBM","MMM", 'M', ExperiencePlus.MagentaDust, 'B', ExperiencePlus.MagentaBlock});
GameRegistry.addRecipe(new ItemStack(MagentaBlock), new Object[] {"MMM", "MMM", "MMM",'M', ExperiencePlus.MagentaDust});
}
#EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
public static CreativeTabs tabBlocksPlus = new CreativeTabs("tabBlocksPlus"){
#Override
public Item getTabIconItem() {
return new ItemStack(MagentaOre).getItem();
}
};
}
You can't edit the current apple item in the game, you would have to create your own one as a forge mod item, which seems like what oyu have done. To make an item give XP, you use this:
player.addExperienceLevel(1); //player is an EntityPlayer or an EntityPlayerMP
SimonApple Item Class
This is what the item class for the SimonApple would look like (without imports):
public class SimonApple extends ItemFood {
private static final int LevelsToAdd = 10; //just an example number; play around with it
public SimonApple(String unlocalizedName, int healAmount, float saturationModifier, boolean wolvesFavorite, CreativeTab creativeTab) {
super(healAmount, saturationModifier, wolvesFavorite);
this.setUnlocalizedName(unlocalizedName);
this.setTextureName(Main.MODID + ":" + unlocalizedName);
this.setCreativeTab(creativeTab);
}
#Override
protected void onFoodEaten(ItemStack stack, World world, EntityPlayer player) {
super.onFoodEaten(stack, world, player);
player.addExperienceLevel(LevelsToAdd);
}
}
Changing Variables in the ExperiencePlus Class
You would need to change this:
public static Item SimonApple;
To something around the lines of this:
public static Item simonApple;
And you would need to change this:
SimonApple = new ItemFood(10, 0.5F, false).setUnlocalizedName("SimonApple").setTextureName("bp:SimonApple").setCreativeTab(tabBlocksPlus);
To just this:
simonApple = new SimonApple("SimonApple", 10, .5f, false, tabBlocksPlus);
NOTE: 10 should be the heal amount, and .5f should be the saturation amount (see table here)
Either create a new item as Abob78 suggests, or create an eventHandler that subscribes to the PlayerUseItem event to be triggered at the Finish state.
To limit the items that that trigger it, you'll have to look at using the itemstack NBT data to store a flag to identify "simon apples" from regular apples. this can be something as simple as a boolean flag such as "isSimonApple"
From the event, you should be able to extract an EntityLiving reference that can be cast to a player reference.
Then either augment the triggering player's XP or spawn XP orbs at their location.
Whatever I try to modify there's always a problem and the program won't run.
The thing is that my program works fine, when it's launched in the console, everything is ok, but when I try to make a GUI, and get text from console in the window, variables doesn't seem to work as they were.
The program is very simple, it has three packages like this:
//class SklepZoologiczny in package sklepzoologiczny
package sklepzoologiczny;
import javax.swing.JFrame;
import zwierzeta.*;
import magazyn.*;
public class SklepZoologiczny {
public static void main(String[] args) {
GUI GUI = new GUI();
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.pack();
GUI.setSize(300, 500);
GUI.setVisible(true);
GUI.setTitle("Appka Zaliczeniowa - Sklep Zoologiczny");
GUI.setResizable(false);
GUI.setLocationRelativeTo(null);
}
}
//class GUI in package sklepzoologiczny
package sklepzoologiczny;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import magazyn.*;
import zwierzeta.*;
public class GUI extends JFrame {
public JLabel l_imie, l_gatunek, l_rasa;
public JButton b_dodaj, b_usun, b_lista;
public JTextField tf_imie, tf_gatunek, tf_rasa;
public String imie, gatunek, rasa;
public ArrayList lista_psow, lista_kotow;
public String pies, kot, gatunek_zwierza;
public String imie_psa, rasa_psa;
public String imie_kota, rasa_kota;
public GUI() {
setLayout(new FlowLayout());
b_dodaj = new JButton("Uruchom Program");
add(b_dodaj);
l_imie = new JLabel("Text from console to GUI should go here");
add(l_imie);
event dodanie = new event();
b_dodaj.addActionListener(dodanie);
}
public class event implements ActionListener {
public void actionPerformed(ActionEvent dodanie) {
magazyn magazyn1 = new magazyn();
magazyn1.kasa = 0;
pies pies1 = new pies();
kot kot1 = new kot();
krolik krolik1 = new krolik();
pies1.ustawImie("Max");
kot1.ustawImie("Nuta");
krolik1.ustawImie("Filip");
pies1.ustawCene(200);
kot1.ustawCene(100);
krolik1.ustawCene(50);
pies1.ustawRase("Jamnik");
kot1.ustawRase("Perski");
krolik1.ustawRase("Mini_Lop");
pies1.ustawGatunek("pies");
kot1.ustawGatunek("kot");
krolik1.ustawGatunek("krolik");
System.out.println("Operacje Zakupu Zwierzat");
System.out.println("---");
magazyn1.dodajZwierza(pies1);
magazyn1.dodajZwierza(kot1);
magazyn1.dodajZwierza(krolik1);
magazyn1.StanSklepu();
System.out.println("Transkacje");
System.out.println("---");
magazyn1.sprzedajZwierza("Nuta");
magazyn1.StanSklepu();
}
}
}
//class magazyn in package magazyn
package magazyn;
import java.util.ArrayList;
import zwierzeta.*;
public class magazyn {
public float kasa;
ArrayList <zwierzeta> lista = new ArrayList(20);
public void dodajZwierza(zwierzeta i){
lista.add(i);
sklepzoologiczny.GUI.l_rasa.setText("Do sklepu dodano zwierza o imieniu: " + i.wezImie());
}
public void sprzedajZwierza(String i){
for(int j=0; j<lista.size(); j++){
if(lista.get(j).wezImie() == i){
kasa = kasa + lista.get(j).wezCene();
lista.remove(j);
System.out.println("Sprzedano: " + i);
}
}
}
public void StanSklepu(){
System.out.println("Aktualny stan sklepu:");
for(int i=0; i<lista.size(); i++){
System.out.println(lista.get(i).wezImie()+", " +lista.get(i).wezGatunek()+", " + lista.get(i).wezRase() + ", cena: " + lista.get(i).wezCene());
}
System.out.println("Stan kasy \t\t\t" + kasa);
}
}
//class zwierzeta in package zwierzeta
package zwierzeta;
public abstract class zwierzeta {
String imie, gatunek, rasa;
float cena;
/* public void checkProduct() throws ProductException{
if(isDamaged == true){
ProductException damaged = new ProductException();
damaged.setErrorMessage("Product is damaged:");
throw damaged;
}
}*/
public void ustawImie(String i){
imie = i;
}
public String wezImie(){
return imie;
}
public void ustawGatunek(String i){
gatunek = i;
}
public String wezGatunek(){
return gatunek;
}
public void ustawRase(String i){
rasa = i;
}
public String wezRase(){
return rasa;
}
public void ustawCene(float i){
cena = i;
}
public float wezCene(){
return cena;
}
}
There are also three classes in package zwierzeta which only extends zwierzeta with no code in it.
So the thing is, whatever I try to put in the dodajZwierza in magazyn.java, there's always an error which says that I can't use non-static variable l_rasa to reference in a static context. I don't know how to fix this, I tried to make class as static in GUI but it just gets worse with more errors.
How can I get the text to appear in the window instead of a console?
First of all - you better avoid using members with names identical to type names:
GUI GUI = new GUI();
You - and the JVM - are more than likely to get confused by this, not knowing whether you are trying to access the class type or the class instance when you later run something like:
GUI.setVisible(true);
Second, if you want to let one class access a member of another class, it is much better to provide a getter that returns (a reference to ) that member, instead of defining the member as static and let the other classes access it directly.
You seem to conflate classes and instances: you want to create an instance of class GUI and then pass this instance around to be able to use the instance rather than the class.
In your main method, you create an instance of class GUI:
GUI GUI = new GUI();
The variable which refers to this instance you call GUI, the same as the class. This is a very bad idea. Java naming conventions dictate that variable names start with a non-capital letter, so you should write:
GUI gui = new GUI();
and change the rest of the main method accordingly.
Now, this instance gui is what you want to use. You have to pass it to the methods where you use it, and then write for example
gui.l_rasa.setText(...);
By the way, your code becomes more maintainable if you make the member variables of a class private, and add getter and setter methods to access them.
You are trying to access non static variable defined in GUI class as:
public JLabel l_imie, l_gatunek, l_rasa;
Here:
sklepzoologiczny.GUI.l_rasa.setText
I dont see its being initialised, but you could define it as static in GUI class like:
public static JLabel l_rasa;//initialize it may be and that would resolve your issue.
Suppose i run my program in eclipse and it'll switch to mozilla window(it is running simultaneously). Similarly when we click a icon in task bar. I have tried Robot class to stimulate click but that's hard-coding coordinates into the program and i don't want to do that.
Any suggestion how i can do this. Thanks.
As far as I understand things, you cannot switch to another running window by name using just core Java. You can swap windows by sending alt-tab keystrokes via a Robot, but this won't bring up a named window. To do this, I recommend using JNI, JNA or some OS-specific utility programming language, such as AutoIt if this were a Windows issue.
For example, using JNA, you could do something like this:
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary;
public class SetForgroundWindowUtil {
public interface User32 extends StdCallLibrary {
User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);
interface WNDENUMPROC extends StdCallCallback {
boolean callback(Pointer hWnd, Pointer arg);
}
boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg);
int GetWindowTextA(Pointer hWnd, byte[] lpString, int nMaxCount);
int SetForegroundWindow(Pointer hWnd);
Pointer GetForegroundWindow();
}
public static boolean setForegroundWindowByName(final String windowName,
final boolean starting) {
final User32 user32 = User32.INSTANCE;
return user32.EnumWindows(new User32.WNDENUMPROC() {
#Override
public boolean callback(Pointer hWnd, Pointer arg) {
byte[] windowText = new byte[512];
user32.GetWindowTextA(hWnd, windowText, 512);
String wText = Native.toString(windowText);
// if (wText.contains(WINDOW_TEXT_TO_FIND)) {
if (starting) {
if (wText.startsWith(windowName)) {
user32.SetForegroundWindow(hWnd);
return false;
}
} else {
if (wText.contains(windowName)) {
user32.SetForegroundWindow(hWnd);
return false;
}
}
return true;
}
}, null);
}
public static void main(String[] args) {
boolean result = setForegroundWindowByName("Untitled", true);
System.out.println("result: " + result);
}
}
I don't know any OS-agnostic way of solving this problem.
I Believe you're question may have been answered here: Active other process's window in Java.
Other than that, JNA would be your best bet for doing this, core java, or java in general doesn't allow
interaction with the operating system directly but JNA does.
The only other way i can think of is call the application, for example chrome with command like arguments(if it takes any) with
try{
Desktop.getDesktop().open(new File("Location\\to\\the\\program.exe"));
} catch(IOException ex){
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
edit:
use this method to call it with parameters
Process process = new ProcessBuilder("Location\\to\\the\\program.exe",
"param1","param2").start();
On linux, try this (this is kotlin code):
val window = "The window name you want to show"
Runtime.getRuntime().exec(arrayOf("/bin/bash", "-c", "wmctrl -a \"$window\""))
Works on Elementary (Loki)
I'm building a basic Point of Sale application and I've been looking for ways of having my main POS JFrame listen for bar code input. I found this code (slightly modified) posted by Cyrusmith, which looks like what I want but I don't know how to implement it in my JFrame. It looks like its intended to be a separate class, which is how I have it in my project currently. I asked my coworker and he doesn't know either.
Thanks for your help.
package barcode;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Listens for bar code input and puts it into a String Buffer.
*
*/
public class BarcodeReader {
private static final long THRESHOLD = 100;
private static final int MIN_BARCODE_LENGTH = 8;
public interface BarcodeListener {
void onBarcodeRead(String barcode);
}
private final StringBuffer barcode = new StringBuffer();
private final List<BarcodeListener> listeners = new CopyOnWriteArrayList<>();
private long lastEventTimeStamp = 0L;
public BarcodeReader() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
try {
if (e.getID() != KeyEvent.KEY_RELEASED) {
return false;
}
if (e.getWhen() - lastEventTimeStamp > THRESHOLD) {
barcode.delete(0, barcode.length());
}
lastEventTimeStamp = e.getWhen();
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
if (barcode.length() >= MIN_BARCODE_LENGTH) {
fireBarcode(barcode.toString());
}
barcode.delete(0, barcode.length());
} else {
barcode.append(e.getKeyChar());
}
return false;
} catch (UnsupportedOperationException err) {
throw new UnsupportedOperationException(err); //To change body of generated methods, choose Tools | Templates.
}
}
});
}
protected void fireBarcode(String barcode) {
for (BarcodeListener listener : listeners) {
listener.onBarcodeRead(barcode);
}
}
public void addBarcodeListener(BarcodeListener listener) {
listeners.add(listener);
}
public void removeBarcodeListener(BarcodeListener listener) {
listeners.remove(listener);
}
}
Most bar code readers basically inject the codes directly into the keyboard buffer. So if you had a JTextField which had keyboard focus, the resulting text would be "entered" directly into it...no magic involved.
If you "want" to use this reader, then you will need to create an instance...
BarcodeReader reader = new BarcodeReader();
Register a BarcodeListener to it...
reader.addBarcodeListener(new BarcodeListener() {
public void onBarcodeRead(String barcode) {
// Respond to the event, like, I don't know,
// set the text of text field :P
}
});
But to me, this just seems like a lot of extra work - but that's just me...
So, yes, it's suppose to be a separate class. Depending on what you want to achieve, you could dump somewhere in your current code base, import the class into your source code and use it like any other. Equally, you could create a separate library for it, but this just means you need to include it within the classpath for compiling and runtime execution as well...
Is there a way I can use Java (or Groovy) to change my desktop wallpaper in Windows XP? I have a program that creates a new image every day (or whenever) and I would like a way to automatically update my desktop.
I've seem some questions on this site about C++ or .NET, but I did not see anything specific to Java.
Sorry I'm a bit behind #ataylor's answer because I was preparing a snippet to do it. Yes, JNA is a correct approach. Here you go:
import java.util.HashMap;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.win32.*;
public class WallpaperChanger {
public static void main(String[] args) {
//supply your own path instead of using this one
String path = "C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg";
SPI.INSTANCE.SystemParametersInfo(
new UINT_PTR(SPI.SPI_SETDESKWALLPAPER),
new UINT_PTR(0),
path,
new UINT_PTR(SPI.SPIF_UPDATEINIFILE | SPI.SPIF_SENDWININICHANGE));
}
public interface SPI extends StdCallLibrary {
//from MSDN article
long SPI_SETDESKWALLPAPER = 20;
long SPIF_UPDATEINIFILE = 0x01;
long SPIF_SENDWININICHANGE = 0x02;
SPI INSTANCE = (SPI) Native.loadLibrary("user32", SPI.class, new HashMap<Object, Object>() {
{
put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
}
});
boolean SystemParametersInfo(
UINT_PTR uiAction,
UINT_PTR uiParam,
String pvParam,
UINT_PTR fWinIni
);
}
}
You need to have the JNA libraries on the classpath for this to work. This was tested in Windows 7, there might be some nuances in XP but I think it should work. That API is presumably stable.
References
Setting Wallpaper - Coding4Fun
How to determine if a screensaver is running in Java?
W32API.java
Edit (2010/01/20):
I had previously omitted the options SPIF_UPDATEINIFILE and SPIF_SENDWININICHANGE. These are now being used as they were suggested in the Coding4Fun MSDN article.
You can do it easier:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.PVOID;
import com.sun.jna.win32.W32APIOptions;
public class Wallpaper {
public static interface User32 extends Library {
User32 INSTANCE = (User32) Native.loadLibrary("user32",User32.class,W32APIOptions.DEFAULT_OPTIONS);
boolean SystemParametersInfo (int one, int two, String s ,int three);
}
public static void main(String[] args) {
User32.INSTANCE.SystemParametersInfo(0x0014, 0, "C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg" , 1);
}
}
You can write a batch file to change the wall-paper, and execute that batch file using,
Runtime.getRuntime.exec()
The JNA java library allows you to easily call Win32 API calls. In particular, to change the desktop background, you need to call the SystemParametersInfo function.
Take a look at this article for an introduction to JNA: http://today.java.net/article/2009/11/11/simplify-native-code-access-jna
Here is a Pure Java implementation which uses Project Panama to make the native callbacks into Windows USER32.DLL. Note that the API is incubating so has changed between JDK16, 17 and later builds. These samples use the versions of Panama that are in the current JDK16/17 release, some changes may be required if you switch to the latest Panama Early Access builds.
import java.lang.invoke.*;
import java.nio.file.Path;
import jdk.incubator.foreign.*;
/**
%JDK16%\bin\java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign SetWallpaper.java A.JPG
*/
public class SetWallpaper {
static final int SPI_SETDESKWALLPAPER = 0x0014;
static final int SPIF_UPDATEINIFILE = 0x01;
static final int SPIF_SENDCHANGE = 0x02;
public static void main(String[] args) throws Throwable {
LibraryLookup user32 = LibraryLookup.ofLibrary("user32");
MethodHandle spi = CLinker.getInstance().downcallHandle(user32.lookup("SystemParametersInfoA").get()
// BOOL SystemParametersInfoA (UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
, MethodType.methodType(int.class, int.class, int.class, MemoryAddress.class, int.class)
, FunctionDescriptor.of(CLinker.C_LONG,CLinker.C_LONG, CLinker.C_LONG, CLinker.C_POINTER, CLinker.C_LONG));
Path path = Path.of(args[0]).toRealPath();
try (NativeScope scope = NativeScope.unboundedScope()) {
MemorySegment img = CLinker.toCString(path.toString(), scope);
int status = (int)spi.invokeExact(SPI_SETDESKWALLPAPER, 0, img.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
System.out.println("Changed wallpaper to "+path+" rc="+status+(status == 0 ? " *** ERROR ***": " OK"));
}
}
}
Small changes needed for JDK17:
/**
%JAVA_HOME%\bin\java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign SetWallpaper.java A.JPG
*/
public static void main(String[] args) throws Throwable {
System.loadLibrary("user32");
// BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
MemoryAddress symbol = SymbolLookup.loaderLookup().lookup("SystemParametersInfoW").get();
MethodHandle SystemParametersInfoW = CLinker.getInstance().downcallHandle(symbol
, MethodType.methodType(int.class, int.class, int.class, MemoryAddress.class, int.class)
, FunctionDescriptor.of(CLinker.C_LONG,CLinker.C_LONG, CLinker.C_LONG, CLinker.C_POINTER, CLinker.C_LONG));
Path path = Path.of(args[0]).toRealPath();
try(ResourceScope scope = ResourceScope.newConfinedScope()) {
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
// toCString as WIDE string
Addressable wide = allocator.allocateArray(CLinker.C_CHAR, (path+"\0").getBytes(StandardCharsets.UTF_16LE));
int status = (int)SystemParametersInfoW.invokeExact(SPI_SETDESKWALLPAPER, 0, wide.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
System.out.println("Changed wallpaper to "+path+" rc="+status+(status == 0 ? " *** ERROR ***": " OK"));
}
}
Expanding on the answer from #DuncG, here's an updated solution which uses Project Panama from JDK 18.
import jdk.incubator.foreign.*;
import java.lang.invoke.MethodHandle;
import java.nio.file.Path;
import static jdk.incubator.foreign.ValueLayout.*;
public class WindowsOperatingSystem {
private static final int SPI_SETDESKWALLPAPER = 0x0014;
private static final int SPIF_UPDATEINIFILE = 0x01;
private static final int SPIF_SENDCHANGE = 0x02;
private static final MethodHandle systemParametersInfoAFunction;
static {
System.loadLibrary("user32");
// BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
systemParametersInfoAFunction = CLinker.systemCLinker().downcallHandle(
SymbolLookup.loaderLookup().lookup("SystemParametersInfoA").get(),
FunctionDescriptor.of(JAVA_BOOLEAN, JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT)
);
}
public static void setWallpaper(Path file) {
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
SegmentAllocator allocator = SegmentAllocator.nativeAllocator(scope);
Addressable nativeFilePath = allocator.allocateUtf8String(file.toString());
var result = (boolean)systemParametersInfoAFunction.invokeExact(
SPI_SETDESKWALLPAPER,
0,
nativeFilePath,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE
);
if (!result) {
throw new IllegalStateException();
}
} catch (Error | RuntimeException t) {
throw t;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}