View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.desktop.os.win;
2   
3   /*
4    * #%L
5    * Reef DB :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.sun.jna.Native;
27  import com.sun.jna.platform.win32.Kernel32;
28  import com.sun.jna.platform.win32.User32;
29  import com.sun.jna.platform.win32.WinDef.*;
30  import com.sun.jna.platform.win32.WinUser.HHOOK;
31  import com.sun.jna.platform.win32.WinUser.HOOKPROC;
32  import com.sun.jna.platform.win32.WinUser.MSG;
33  import fr.ifremer.quadrige3.ui.swing.desktop.DesktopPower;
34  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.handle.CWPSSTRUCT;
35  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.handle.HANDLER_ROUTINE;
36  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.handle.WNDPROC;
37  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.libs.Kernel32Ex;
38  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.wrap.GetLastErrorException;
39  import fr.ifremer.quadrige3.ui.swing.desktop.os.win.wrap.WNDCLASSEXWrap;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  import javax.swing.JFrame;
44  import javax.swing.JOptionPane;
45  
46  /**
47   * <p>WindowsPower class.</p>
48   *
49   */
50  public class WindowsPower extends DesktopPower {
51  
52      private static final Log LOG = LogFactory.getLog(WindowsPower.class);
53  
54      /** Constant <code>WM_QUERYENDSESSION=17</code> */
55      private static final int WM_QUERYENDSESSION = 17;
56      /** Constant <code>WM_ENDSESSION=22</code> */
57      private static final int WM_ENDSESSION = 22;
58      /** Constant <code>WH_CALLWNDPROC=4</code> */
59      private static final int WH_CALLWNDPROC = 4;
60  
61      public class MessagePump implements Runnable {
62          final Thread t;
63  
64          WNDCLASSEXWrap wc;
65          WNDPROC WndProc;
66          HWND hWnd;
67          HINSTANCE hInstance;
68  
69          final Object lock = new Object();
70  
71          public MessagePump() {
72              t = new Thread(this, WindowsPower.class.getSimpleName());
73          }
74  
75          public void start() {
76              synchronized (lock) {
77                  t.start();
78                  try {
79                      lock.wait();
80                  } catch (InterruptedException e) {
81                      Thread.currentThread().interrupt();
82                  }
83              }
84          }
85  
86          void create() {
87              WndProc = (hWnd, msg, wParam, lParam) -> {
88                  switch (msg) {
89                      case WM_ENDSESSION:
90                          return new LRESULT(0);
91                      case WM_QUERYENDSESSION:
92                          JOptionPane.showMessageDialog(null, "exit");
93                          callListeners("WM_QUERYENDSESSION callback");
94                          return new LRESULT(0);
95                      case User32.WM_QUIT:
96                          User32.INSTANCE.PostMessage(hWnd, User32.WM_QUIT, null, null);
97                          break;
98                  }
99  
100                 return User32.INSTANCE.DefWindowProc(hWnd, msg, wParam, lParam);
101             };
102             hWnd = createWindow();
103         }
104 
105         // http://osdir.com/ml/java.jna.user/2008-07/msg00049.html
106 
107         HWND createWindow() {
108             hInstance = Kernel32.INSTANCE.GetModuleHandle(null);
109 
110             wc = new WNDCLASSEXWrap(hInstance, WndProc, WindowsPower.class.getSimpleName());
111 
112             HWND hwnd = User32.INSTANCE.CreateWindowEx(0, wc.getClassName().toString(), wc.getName(), User32.WS_OVERLAPPED, 0, 0,
113                     0, 0, null, null, hInstance, null);
114 
115             if (hwnd == null)
116                 throw new GetLastErrorException();
117 
118             return hwnd;
119         }
120 
121         @Override
122         public void run() {
123             create();
124 
125             synchronized (lock) {
126                 lock.notifyAll();
127             }
128 
129             MSG msg = new MSG();
130 
131             while (User32.INSTANCE.GetMessage(msg, null, 0, 0) > 0) {
132                 User32.INSTANCE.DispatchMessage(msg);
133             }
134 
135             destory();
136         }
137 
138         void destory() {
139             if (hWnd != null) {
140                 if (!User32.INSTANCE.DestroyWindow(hWnd))
141                     throw new GetLastErrorException();
142                 hWnd = null;
143             }
144 
145             if (wc != null) {
146                 wc.close();
147                 wc = null;
148             }
149         }
150 
151         void close() {
152             User32.INSTANCE.PostQuitMessage(0);
153 
154             try {
155                 if (!Thread.currentThread().equals(t))
156                     t.join();
157             } catch (InterruptedException e) {
158                 Thread.currentThread().interrupt();
159             }
160         }
161     }
162 
163     private final MessagePump mp = new MessagePump();
164 
165     private final HANDLER_ROUTINE hr = dwCtrlType -> {
166 
167         if ((dwCtrlType & HANDLER_ROUTINE.CTRL_CLOSE_EVENT) == HANDLER_ROUTINE.CTRL_CLOSE_EVENT) {
168             callListeners("HANDLER_ROUTINE.CTRL_CLOSE_EVENT");
169         }
170         if ((dwCtrlType & HANDLER_ROUTINE.CTRL_LOGOFF_EVENT) == HANDLER_ROUTINE.CTRL_LOGOFF_EVENT) {
171             callListeners("HANDLER_ROUTINE.CTRL_LOGOFF_EVENT");
172         }
173         if ((dwCtrlType & HANDLER_ROUTINE.CTRL_SHUTDOWN_EVENT) == HANDLER_ROUTINE.CTRL_SHUTDOWN_EVENT) {
174             callListeners("HANDLER_ROUTINE.CTRL_SHUTDOWN_EVENT");
175         }
176         return 1;
177     };
178 
179     private final HOOKPROC hp = new HOOKPROC() {
180         @SuppressWarnings("unused")
181         public LRESULT callback(int nCode, WPARAM wParam, CWPSSTRUCT hookProcStruct) {
182             switch (hookProcStruct.message) {
183                 case WM_QUERYENDSESSION:
184                     callListeners("WM_QUERYENDSESSION hook");
185                     break;
186             }
187             return new LRESULT();
188         }
189     };
190     private HHOOK hHook;
191     private JFrame f = new JFrame();
192 
193     /**
194      * <p>Constructor for WindowsPower.</p>
195      */
196     public WindowsPower() {
197         if (!Kernel32Ex.INSTANCE.SetProcessShutdownParameters(0x03FF, 0))
198             throw new GetLastErrorException();
199 
200         mp.start();
201 
202         if (!Kernel32Ex.INSTANCE.SetConsoleCtrlHandler(hr, true))
203             throw new GetLastErrorException();
204 
205         final HWND hwnd = new HWND();
206         f.pack();
207         hwnd.setPointer(Native.getComponentPointer(f));
208 
209         int wID = User32.INSTANCE.GetWindowThreadProcessId(hwnd, null);
210         hHook = User32.INSTANCE.SetWindowsHookEx(WH_CALLWNDPROC, hp, null, wID);
211         if (hHook == null)
212             throw new GetLastErrorException();
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public void close() {
218         if (!User32.INSTANCE.UnhookWindowsHookEx(hHook))
219             throw new GetLastErrorException();
220 
221         f.dispose();
222         f = null;
223 
224         mp.close();
225 
226         if (!Kernel32Ex.INSTANCE.SetConsoleCtrlHandler(hr, false))
227             throw new GetLastErrorException();
228     }
229 
230     /**
231      * <p>callListeners.</p>
232      *
233      * @param source a {@link java.lang.String} object.
234      */
235     protected void callListeners(String source) {
236 
237         if (LOG.isDebugEnabled()) LOG.debug("call listeners from " + source);
238 
239         for (Listener l : listeners) {
240             l.quit();
241         }
242 
243     }
244 }