諸事情でメインのディスプレイをセカンダリに、小さなディスプレイをプライマリに設定して使うことにした。
久しぶりに画像を表示する自作のJavaプログラムを起動すると、小さなディスプレイではうまく動かない。この不具合にも対策が必要だが、どちらにしても、大きい方のディスプレイを使うようにプログラムしたい。
環境を再び替えることもあろうから、セカンダリと決めてかかるのはよくない。今回の問題は高さだったので、縦の解像度の大きい方ということにした。
概要は
以上についてプログラムを紹介する
追記
JFrame#setLocationByPlatform(true);を指定しておくことで、javaプログラムの起動端末の表示されているスクリーンが自動的に選択されるらしいとわかった。デフォルトのスクリーン内でのおまかせ配置だと思っていた。ずっと昔にOSによっては動かなかったり、配置が気に入らなかったりしてずっと使っていなかったので、あまり期待をしていなかったというので発見が遅れた。きっかけとなったプログラムにこれが書いてあったら、今回の調査に至らなかったが、やれることが広がったので良しとする。
GraphicsEnvironment#getScreenDevices()でGraphicsDeviceの配列を得る。
GraphicsEnvironment#getDefaultScreenDevice()でデフォルトのGraphicsDeviceを得る。
import java.awt.GraphicsEnvironment; import java.awt.GraphicsDevice; public class ScreenTest00{ public static void main (String [] args) { //GG java.awt.GraphicsDevice GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); System.out.println("Env:"+ genv.toString()); GraphicsDevice[] gdevs = genv.getScreenDevices(); System.out.println(gdevs.length + " ScreenDevices found."); for(GraphicsDevice gdev: gdevs){ System.out.println(gdev.toString()); } GraphicsDevice ddev = genv.getDefaultScreenDevice(); System.out.println("Default Device is : "+ ddev.toString() ); } }
GraphicsDeviceは、画面、プリンタ、またはイメージ・バッファの場合があると書いてあるが、画面だけ、つまり、Screen0,1が得られる。
デフォルトは0である。
adachi@debian64:/media/adachi/S1T/m06/jpgseldlg$ java ScreenTest00 Env:sun.awt.X11GraphicsEnvironment@7ba4f24f 2 ScreenDevices found. X11GraphicsDevice[screen=0] X11GraphicsDevice[screen=1] Default Device is : X11GraphicsDevice[screen=0]
各GraphicsDeviceは、それに関連したいくつかのGraphicsConfigurationオブジェクトを持つということなので、調べる。
GraphicsDevice#getConfigurations()でGraphicsConfigurationの配列を得る
GraphicsDevice#getDefaultConfiguration()でデフォルトのGraphicsConfigurationを得る
import java.awt.Point; import java.awt.Rectangle; import java.util.TreeMap; import java.awt.GraphicsEnvironment; import java.awt.GraphicsDevice; import java.awt.GraphicsConfiguration; public class ScreenTest01 { public static void main (String [] args) { GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gdevs = genv.getScreenDevices(); System.out.println(gdevs.length + " ScreenDevices found."); TreeMapgdevmap = new TreeMap<>(); Rectangle virtualBounds = new Rectangle(); for(GraphicsDevice gdev: gdevs){ System.out.println(gdev.toString()); GraphicsConfiguration[] gcnfs = gdev.getConfigurations(); System.out.println(gcnfs.length + " Configurations"); int ct = 0; for(GraphicsConfiguration gcnf: gcnfs){ ct++; Rectangle rectn = gcnf.getBounds(); System.out.println("Config:"+ct+":"+gcnf.toString() ); System.out.println(" bounds:"+ rectn.toString() ); if (ct>4) { System.out.println("...."); break; }//if }//for GraphicsConfiguration dcnf = gdev.getDefaultConfiguration(); Rectangle drectn = dcnf.getBounds(); System.out.println("Config:D:"+ dcnf.toString() ); System.out.println(" bBounds:"+ drectn.toString() ); virtualBounds = virtualBounds.union(drectn); gdevmap.put(drectn.getHeight(),dcnf); } System.out.println("VBounds:"+ virtualBounds.toString() ); System.out.println("lastConf:"+ gdevmap.lastEntry().getValue()); Point p = genv.getCenterPoint(); Rectangle rec = genv.getMaximumWindowBounds(); System.out.println(p); System.out.println(rec); } }
Deviceごとに240のConfigurationがある。この出力ではvisの値だけが異なる。5番目まで表示させているがこの後も同じ。
adachi@debian64:/media/adachi/S1T/m06/jpgseldlg$ java ScreenTest01 2 ScreenDevices found. X11GraphicsDevice[screen=0] 240 Configurations Config:1:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x21] bounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] Config:2:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2c5] bounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] Config:3:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2c6] bounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] Config:4:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2c7] bounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] Config:5:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2c8] bounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] .... Config:D:X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x21] bBounds:java.awt.Rectangle[x=1920,y=176,width=1280,height=1024] X11GraphicsDevice[screen=1] 240 Configurations Config:1:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x21] bounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] Config:2:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x2c5] bounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] Config:3:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x2c6] bounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] Config:4:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x2c7] bounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] Config:5:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x2c8] bounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] .... Config:D:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x21] bBounds:java.awt.Rectangle[x=0,y=0,width=1920,height=1200] VBounds:java.awt.Rectangle[x=0,y=0,width=3200,height=1200] lastConf:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x21] java.awt.Point[x=2560,y=688] java.awt.Rectangle[x=1920,y=176,width=1280,height=1024]
API仕様には各Confを使ってJFrameオブジェクトを生成する例が出ている。
どれも同じなので、それぞれのデバイスのデフォルトのConfだけを使って、TreeMapでheightが最大なもの(last)を得ている。
virtualBounds.union(drectn)で、合成された仮想スクリーンを得ている
合成[x=0,y=0,width=3200,height=1200] dev1[x=0,y=0,width=1920,height=1200],dev2[x=1920,y=176,width=1280,height=1024]
最後は、GraphicsEnvironment#getCenterPoint()とGraphicsEnvironment#getMaximumWindowBounds()だが、仮想デバイスのセンターではなく、仮想デバイスの座標で見たデフォルトデバイスのセンターの座標と、最大のサイズということらしい。このプログラムで出てくる値はすべて仮想デバイスの座標だけれども。
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.awt.GraphicsEnvironment; import java.awt.GraphicsDevice; import java.awt.GraphicsConfiguration; public class ScreenTest02 { public ScreenTest02(){ //GG java.awt.GraphicsDevice GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gdevs = genv.getScreenDevices(); TreeMapgdevmap = new TreeMap<>(); for(GraphicsDevice gdev: gdevs){ GraphicsConfiguration dcnf = gdev.getDefaultConfiguration(); Rectangle drectn = dcnf.getBounds(); System.out.println("["+gdev.getIDstring()+"]"+ "["+drectn.getX()+","+drectn.getX()+","+drectn.getWidth()+","+drectn.getHeight()+"]"); gdevmap.put(drectn.getHeight(),dcnf); } GraphicsConfiguration selectedConf = gdevmap.lastEntry().getValue(); System.out.println("lastConf:"+ selectedConf); JFrame frame = new JFrame(selectedConf); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); frame.setLocationByPlatform(true); //frame.pack(); frame.setVisible(true); } public static void main (String [] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new ScreenTest02(); } }); } }
これで空のJFrameが1920x1200の方へ表示される。
adachi@debian64:/media/adachi/S1T/m06/jpgseldlg$ java ScreenTest02 [:0.0][1920.0,1920.0,1280.0,1024.0] [:0.1][0.0,0.0,1920.0,1200.0] lastConf:X11GraphicsConfig[dev=X11GraphicsDevice[screen=1],vis=0x21]
複数あるスクリーン間をJFrameが移動するプログラムを作ってみる。
表示後は仮想スクリーン内の座標になるので、GraphicsConfigurationで切り替えるのではなく、単なる座標指定となる。
今回の例では、1280x1024と1920x1200が合成されて、3200x1200のスクリーンになっている。
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.awt.GraphicsEnvironment; import java.awt.GraphicsDevice; import java.awt.GraphicsConfiguration; public class ScreenTest03 implements ActionListener { java.util.Listgclist; int gcn; JTextArea textarea; JFrame frame; public ScreenTest03(){ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gdevs = genv.getScreenDevices(); TreeMap gdevmap = new TreeMap<>(); for(GraphicsDevice gdev: gdevs){ GraphicsConfiguration dcnf = gdev.getDefaultConfiguration(); Rectangle drectn = dcnf.getBounds(); System.out.println("["+gdev.getIDstring()+"]"+ "["+drectn.getX()+","+drectn.getY()+ ","+drectn.getWidth()+","+drectn.getHeight()+"]"); gdevmap.put(drectn.getHeight(),dcnf); } //GraphicsConfiguration[] gcs = gdevmap.values().toArray(new GraphicsConfiguration[0]); //List gclist = new LinkedList(gdevmap.values()); gclist = new LinkedList<>(gdevmap.values()); gcn = gclist.size()-1; GraphicsConfiguration selectedConf = gclist.get(gcn); System.out.println("lastConf:"+ selectedConf); frame = new JFrame(selectedConf); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); frame.setLocationByPlatform(true); textarea = new JTextArea(); textarea.setLineWrap(true); //自動的に改行するように JScrollPane scrollpane = new JScrollPane(textarea); scrollpane.setPreferredSize(new Dimension(400, 200)); JButton mvbttn = new JButton("move"); mvbttn.setActionCommand("move"); mvbttn.addActionListener(this); JButton endbttn = new JButton("終了"); endbttn.setActionCommand("quit"); endbttn.addActionListener(this); frame.add(mvbttn,BorderLayout.NORTH); frame.add(scrollpane,BorderLayout.CENTER); frame.add(endbttn,BorderLayout.SOUTH); frame.setVisible(true); System.out.println("location:"+ frame.getX() + ","+ frame.getY() ); System.out.println("location:"+ frame.getLocationOnScreen() ); System.out.println("location:"+ frame.getLocation() ); } public void actionPerformed( ActionEvent e ){ if ("quit".equals(e.getActionCommand())) { System.exit(0); }else if ("move".equals(e.getActionCommand())) { textarea.append(frame.getX() + ","+ frame.getY()+"\n"); //textarea.append("locationOns:"+ frame.getLocationOnScreen()+"\n" ); //textarea.append("location:"+ frame.getLocation() +"\n"); GraphicsConfiguration gc = gclist.get(gcn); Rectangle rect = gc.getBounds(); int dx = frame.getX()-rect.x; //Rectangle#getX()はdouble int dy = frame.getY()-rect.y; textarea.append(rect.x + "," + dx +"," + rect.y + "," + dy+"\n"); gcn=++gcn%gclist.size(); gc = gclist.get(gcn); rect = gc.getBounds(); frame.setLocation(rect.x+dx,rect.y+dy); } } public static void main (String [] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new ScreenTest03(); } }); } }
actionPerformed()内で、個別スクリーンの左上の座標を求めて、現在の位置から引いて、個別スクリーン内の位置をdx,dyに格納。その後次のGraphicsConfigurationから移動先のスクリーンの左上座標を求め、dx,dyを足して移動先とする。
JFrame#getLocationOnScreen()もJFrame#getLocation()も変わりない。