Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Full-Screen Exclusive Mode и javax.swing.Timer, Ошибка java.lang.NullPointerException 
:(
    Опции темы
lilyam
  Дата 4.4.2011, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 13.10.2010

Репутация: нет
Всего: нет



Приветствую!
Вот решил поработать с 2d графикой, начал с книги Developing Games in Java от David Brackeen. В первых главах в книге представлен класс для работы в полноэкранном режиме с использованием Full-Screen Exclusive Mode: http://pastie.org/1753701. Для анимации я решил воспользоваться классом javax.swing.Timer: расширил класс ScreenManager(http://pastie.org/1753701) внутренним классом, который реализует, соответственно, интерфейс ActionListener:
Код

package screenmanager;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.Timer;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
    The ScreenManager class manages initializing and displaying
    full screen graphics modes.
*/
public class ScreenManager {

    private GraphicsDevice device = null;
    private ActionListener scr_list = null;
    private Timer tm = null;

    /**
        Creates a new ScreenManager object.
    */
    public ScreenManager() {
        GraphicsEnvironment environment =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        device = environment.getDefaultScreenDevice();
    }


    /**
        Returns a list of compatible display modes for the
        default device on the system.
    */
    public DisplayMode[] getCompatibleDisplayModes() {
        return device.getDisplayModes();
    }


    /**
        Returns the first compatible mode in a list of modes.
        Returns null if no modes are compatible.
    */
    public DisplayMode findFirstCompatibleMode(
        DisplayMode modes[])
    {
        DisplayMode goodModes[] = device.getDisplayModes();
        for (int i = 0; i < modes.length; i++) {
            for (int j = 0; j < goodModes.length; j++) {
                if (displayModesMatch(modes[i], goodModes[j])) {
                    return modes[i];
                }
            }

        }

        return null;
    }


    /**
        Returns the current display mode.
    */
    public DisplayMode getCurrentDisplayMode() {
        return device.getDisplayMode();
    }


    /**
        Determines if two display modes "match". Two display
        modes match if they have the same resolution, bit depth,
        and refresh rate. The bit depth is ignored if one of the
        modes has a bit depth of DisplayMode.BIT_DEPTH_MULTI.
        Likewise, the refresh rate is ignored if one of the
        modes has a refresh rate of
        DisplayMode.REFRESH_RATE_UNKNOWN.
    */
    public boolean displayModesMatch(DisplayMode mode1,
        DisplayMode mode2)
    {
        if (mode1.getWidth() != mode2.getWidth() ||
            mode1.getHeight() != mode2.getHeight())
        {
            return false;
        }

        if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
            mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
            mode1.getBitDepth() != mode2.getBitDepth())
        {
            return false;
        }

        if (mode1.getRefreshRate() !=
            DisplayMode.REFRESH_RATE_UNKNOWN &&
            mode2.getRefreshRate() !=
            DisplayMode.REFRESH_RATE_UNKNOWN &&
            mode1.getRefreshRate() != mode2.getRefreshRate())
         {
             return false;
         }

         return true;
    }


/**
        Enters full screen mode and changes the display mode.
        If the specified display mode is null or not compatible
        with this device, or if the display mode cannot be
        changed on this system, the current display mode is used.
        <p>
        The display uses a BufferStrategy with 2 buffers.
    */
    public void setFullScreen(DisplayMode displayMode) {
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setIgnoreRepaint(true);
        frame.setResizable(false);
        
        device.setFullScreenWindow(frame);
        if (displayMode != null &&
            device.isDisplayChangeSupported())
        {
            try {
                device.setDisplayMode(displayMode);
            }
            catch (IllegalArgumentException ex) { }
        }
        frame.createBufferStrategy(2);
        
        scr_list = new ScreenListener();

        tm = new Timer(100,  scr_list);
    tm.start();
    }


    /**
        Gets the graphics context for the display. The
        ScreenManager uses double buffering, so applications must
        call update() to show any graphics drawn.
        <p>
        The application must dispose of the graphics object.
    */
    public Graphics2D getGraphics() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            BufferStrategy strategy = window.getBufferStrategy();
            return (Graphics2D)strategy.getDrawGraphics();
        }
        else {
            return null;
        }
    }


    /**
        Updates the display.
    */
    public void update() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            BufferStrategy strategy = window.getBufferStrategy();
            if (!strategy.contentsLost()) {
                strategy.show();
            }
        }
        // Sync the display on some systems.
        // (on Linux, this fixes event queue problems)
        Toolkit.getDefaultToolkit().sync();
    }


    /**
        Returns the window currently used in full screen mode.
        Returns null if the device is not in full screen mode.
    */
    public Window getFullScreenWindow() {
        return device.getFullScreenWindow();
    }


    /**
        Returns the width of the window currently used in full
        screen mode. Returns 0 if the device is not in full
        screen mode.
    */
    public int getWidth() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            return window.getWidth();
        }
        else {
            return 0;
        }
    }


    /**
        Returns the height of the window currently used in full
        screen mode. Returns 0 if the device is not in full
        screen mode.
    */
    public int getHeight() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            return window.getHeight();
        }
        else {
            return 0;
        }
    }


    /**
        Restores the screen's display mode.
    */
    public void restoreScreen() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            window.dispose();
        }
        device.setFullScreenWindow(null);
    }


    /**
        Creates an image compatible with the current display.
    */
    public BufferedImage createCompatibleImage(int w, int h,
        int transparency)
    {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            GraphicsConfiguration gc =
                window.getGraphicsConfiguration();
            return gc.createCompatibleImage(w, h, transparency);
        }
        return null;
    }
    
    public void stopTimer()
    {
     if(tm != null)
         tm.stop();
    }
    
    
    class ScreenListener implements ActionListener
    {
     public void actionPerformed(ActionEvent e)
     {
         Graphics gr = getGraphics();
         
         Window win = device.getFullScreenWindow();
         win.setBackground(Color.blue);
         win.setForeground(Color.WHITE);
         
         gr.drawString("Timer in", 100, 100);
         gr.dispose();
         update();
     }
    }
}

При вызове метода getFullScreenWindow получаю null.  Почему? 
Тестовый класс:
Код

package swingtimertest;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import screenmanager.*;

import java.awt.DisplayMode;

public class SwingTimerTest implements KeyListener 
{
    public synchronized void keyPressed(KeyEvent e)
    {
        int key_code = e.getKeyCode();
        if(key_code == KeyEvent.VK_ESCAPE)
            isStop = true;
        e.consume();    
    }
    
    public void keyTyped(KeyEvent e)
    {
        e.consume();
    }
    
    public void keyReleased(KeyEvent e)
    {
        e.consume();
    }
    
    static public void main(String[] args)
    {
        ScreenManager disp = null;
        DisplayMode dm = null;
        DisplayMode possible_dms[] = {
        new DisplayMode(1280, 1024, 32, 75)
        };
        
        try
        {
            disp = new ScreenManager();
            dm = disp.findFirstCompatibleMode(possible_dms);
            disp.setFullScreen(dm);
            
            if(isStop)
                           disp.stopTimer();
        }
        finally
        {
            disp.restoreScreen();
        }
    }
    
    private static boolean isStop = false;
}

Откуда видно, что прежде чем запустить метод setFullScreen инициализируется объект типа ScreenManager, с полями которого и работает листенёр(классса ScreenListener). но когда выполняется метод actionPerformed, запущенный после создания листенёра и старта таймера,  getFullScreenWindow возвращает ноль, что говорит о том, что режим не полноэкранный:
Код

    public Graphics2D getGraphics() {
        Window window = device.getFullScreenWindow();
        if (window != null) {
            BufferStrategy strategy = window.getBufferStrategy();
            return (Graphics2D)strategy.getDrawGraphics();
        }
        else {
            return null;
        }
    }

, хотя он был установлен методом setFullScreen: 
Код

/**
        Enters full screen mode and changes the display mode.
        If the specified display mode is null or not compatible
        with this device, or if the display mode cannot be
        changed on this system, the current display mode is used.
        <p>
        The display uses a BufferStrategy with 2 buffers.
    */
    public void setFullScreen(DisplayMode displayMode) {
        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setIgnoreRepaint(true);
        frame.setResizable(false);
        
        device.setFullScreenWindow(frame);
        if (displayMode != null &&
            device.isDisplayChangeSupported())
        {
            try {
                device.setDisplayMode(displayMode);
            }
            catch (IllegalArgumentException ex) { }
        }
        frame.createBufferStrategy(2);
        
        scr_list = new ScreenListener();

        tm = new Timer(100,  scr_list);
    tm.start();
    }


Это сообщение отредактировал(а) lilyam - 4.4.2011, 20:22
PM MAIL   Вверх
lilyam
Дата 4.4.2011, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 13.10.2010

Репутация: нет
Всего: нет



Замечу, если использовать обычное прерывание текущего потока, то всё работает
Код

...
try
{
    disp = gdev.getFullScreenWindow();
    if(disp == null)
    {
    System.err.println("Error!");
    System.exit(0);
    }
    disp.setVisible(true);
    BufferStrategy buff_strategy = disp.getBufferStrategy();
    Graphics gr = buff_strategy.getDrawGraphics();
    draw(gr);
    gr.dispose();
    buff_strategy.show();
    Thread.sleep(3000);
}
catch(InterruptedException e)
    {}
finally
    {
    disp.setVisible(false);
    disp.dispose();
    gdev.setFullScreenWindow(null);
    }
...

PM MAIL   Вверх
jk1
Дата 5.4.2011, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 21
Всего: 75



Я не совсем понял, что Вы называете "перыванием текущего потока", так как вызова метода interrupt() в Вашем коде нет.

Тем не менее попытаюсь объяснить происходящее. На самом деле тут учавствуют три потока: 

1. начальный, в котором запускается метод main() 
2. поток, в котором ждут таймеры  
3. EDT, в котором, как следует из документации, всегды выполняются действия javax.swing.Timer.

Вы запускаете таймер с очень маленькой начальной задержкой, он срабатывает на EDT и к этому времени асинхронная установка графического режима просто не успевает отработать. Но начальная задержка при запуске таймера - это плохой выход, ведь на разных машинах на смену графического режима может уйти разное количество времени.

Вот небольшой фикс, который проблему решает:
Код

class ScreenListener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            Graphics gr = getGraphics();

            Window win = device.getFullScreenWindow();
            if (win != null) {
                win.setBackground(Color.blue);
                win.setForeground(Color.WHITE);

                gr.drawString("Timer in " + i++, 100, 100);
                gr.dispose();
                update();
            }
        }
    }


Как видите, он просто пропускает несколько первых тактов до тех пор, пока полноэкранный режим не будет поинициализирован до конца.

Правда анимации все равно не получается, ведь начальный поток быстро завершает свое выполнение и возвращает прежний режим.
Тут уже двумя строчками все не поправишь, нужно организовывать нормальное управление потоками Вашего приложения.

Это сообщение отредактировал(а) jk1 - 5.4.2011, 14:49


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
lilyam
Дата 10.4.2011, 14:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 13.10.2010

Репутация: нет
Всего: нет



to jk1
Т.е. нужно работать, как вы написали, с тремя потоками: главный, поток, в котором ожидает таймер и EDT-поток, в котором выполняется actionPerformed, правильно я вас понимаю? А как мне получить ссылки на две последней нити? 
А можно ли использовать SwingWorker: перевести дисплей в полноэкранный режим в doInBackground в связке с done и get, как только был установлен необходимый графический режим, оповестить EDT, и только после этого запустить таймер?
С SwingWorker заработало:
Код

package fullscreenem;

import java.util.Date;
import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsDevice;
import java.awt.DisplayMode;
import java.awt.Window;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.SwingWorker;

public class FullScreenEM 
{
    public static void main(String[] args)
    {
        new FullScreenEM().run(1000, 1000);    
    }
    
    public FullScreenEM( )
    {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        gdev = ge.getDefaultScreenDevice();
    }
    /**
    with run: 
    -create our ActionListener(ScreenTimer); 
    -determine worker(called init()-method to get ref to full screen window), returning result of this operation to EDT;
    -create and start timer;
    -execute our worker.
    */
    public void run(int pause, int delay)
    {
        worker = new SwingWorker<Window, Void>() 
        {
                public Window doInBackground() 
                {
                 init();
                    return gdev.getFullScreenWindow();
                }

                public void done() 
                {
                    try {
                        disp = get();
                    } 
                    catch (InterruptedException ignore) {}
                    catch (java.util.concurrent.ExecutionException e) {
                        String why = null;
                        Throwable cause = e.getCause();
                        if (cause != null) {
                            why = cause.getMessage();
                        } else {
                            why = e.getMessage();
                        }
                        System.err.println("Error retrieving file: " + why);
                    }
                }
        };
        
        try
        {    
            screen_list = new ScreenListener();
            tm = new Timer(delay, screen_list);
            tm.setInitialDelay(pause);
            tm.setRepeats(true);
            tm.start();
            worker.execute();
        }
        catch(Exception e)
        {}
        finally
        {
            if(!tm.isRunning())
            {
                disp = gdev.getFullScreenWindow();
                disp.setVisible(false);
                disp.dispose();
                gdev.setFullScreenWindow(null);
            }
        }
            
    }
    /*
     * initialize: 
    -create jframe;
    -apply it to our Window ref(disp);
    -choice DisplayMode and set it to our graphic_device(gdev);
    -create buffer strategy for our disp
    */
    private void init()
    {
        JFrame frame = new JFrame();
        frame.setResizable(false);
        frame.setIgnoreRepaint(true);
        frame.setUndecorated(true);
        disp = new Window(frame);
        
        DisplayMode[] dms = gdev.getDisplayModes();
        
        DisplayMode use_dm = null;
        
        if(gdev.isFullScreenSupported())
        {
            disp.setBackground(Color.CYAN);
            disp.setForeground(Color.WHITE);
            gdev.setFullScreenWindow(disp);
        }
        
        use_dm = getMatchMode(dms, def_dm);
        
        try 
        {
            disp = gdev.getFullScreenWindow();
            if(disp != null)
            {
                disp.setVisible(true);
                gdev.setDisplayMode(use_dm);
                disp.createBufferStrategy(2);
                disp.setFocusTraversalKeysEnabled(false);
                disp.addKeyListener((KeyListener)screen_list);
            }    
        }
        catch(IllegalArgumentException ex) 
        { 
            
        }    
    }
    
    /**
    Updates the display.
    */
    public void update() 
    {
        Window disp = gdev.getFullScreenWindow();
        if (disp != null)
        {
            BufferStrategy strategy = disp.getBufferStrategy();
            if (!strategy.contentsLost()) 
            {
                strategy.show();
            }
        }
        // Sync the display on some systems.
        // (on Linux, this fixes event queue problems)
        Toolkit.getDefaultToolkit().sync();
    }

    
    /*
     * actionPerformed:
     -if our routine with setting full screen was done draw anything in our disp;
     * keyPressed:
     -if "esc" was pressed timer should have stopped
     */
    class ScreenListener implements ActionListener, KeyListener 
    {
        public void actionPerformed(ActionEvent e)
        {
            if(!worker.isDone())
            {
                return;
            }
            else
            {
                disp = gdev.getFullScreenWindow();
                if (disp != null) 
                {
                    BufferStrategy start_strategy = disp.getBufferStrategy();
                    Graphics2D gr = (Graphics2D)start_strategy.getDrawGraphics();                
                    draw(gr);
                    gr.dispose();
                    
                    disp = gdev.getFullScreenWindow();
                    
                    if(disp != null) 
                    {
                        BufferStrategy end_strategy = disp.getBufferStrategy();
                        if (!end_strategy.contentsLost()) 
                        {
                         end_strategy.show();
                        }
                    }
                    // Sync the display on some systems.
                    // (on Linux, this fixes event queue problems)
                    Toolkit.getDefaultToolkit().sync();
                }
            }
        }
        
        public void start()
        {
            if(worker.isDone())
            {
                tm.restart();
            }
        }
        
        private void draw(Graphics2D gr)
        {
            DisplayMode curr_dm = gdev.getDisplayMode();
            
            gr.drawString("Time is "+ new Date(), curr_dm.getWidth()/2, curr_dm.getHeight()/2);
        }
        
        public void keyPressed(KeyEvent key_ev)
        {    
            key_ev.consume();
        }
        
        public void keyReleased(KeyEvent key_ev)
        {
            key_ev.consume();
        }
        
        public void keyTyped(KeyEvent key_ev)
        {
            if(key_ev.getKeyCode() == KeyEvent.VK_ESCAPE)
            {
                tm.stop();
            }
            key_ev.consume();
        }
    }
    
    private DisplayMode getMatchMode(DisplayMode[] dms, DisplayMode dm)
    {
        boolean f = false;
        
        for(int i = 0; i < dms.length && !f; i++)
            if(dms[i].equals(dm) && gdev.isDisplayChangeSupported())
            {
                gdev.setDisplayMode(dm);
                f = true;
            }
        if(f)
            return(dm);
        return(null);
    }
    
    private GraphicsDevice gdev = null;
    private Window disp = null;
    private ActionListener screen_list = null;
    private Timer tm = null;
    private SwingWorker worker = null;
    
    private final int TEST_BIT_DEPTH = 32;
    private final int TEST_REFRESH = 75;
    private final int DISPLAY_WIDTH = 1280;
    private final int DISPLAY_HEIGHT = 1024;
    private final DisplayMode def_dm = new DisplayMode(DISPLAY_WIDTH, DISPLAY_HEIGHT, TEST_BIT_DEPTH, TEST_REFRESH);
}

Спасибо, за отклик. Думаю, вопросы ещё появятся)

Это сообщение отредактировал(а) lilyam - 10.4.2011, 21:43
PM MAIL   Вверх
lilyam
Дата 10.4.2011, 21:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 13.10.2010

Репутация: нет
Всего: нет



to jk1
А вот собственно и вопросы)
Почему-то keyListener не работает: в отладчике при нажатии любой клавиши должен забегать в keyPressed, но нажатие игнорируется.
И не перерисовывается должным образом дата, вместо этого текущую дату рисует за место прошлой, не затирая заранее последнюю. 
Никаких ошибок не выдает по окончанию работы. Хелп smile 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   jk1
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, jk1.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: GUI и Java FX приложения | Следующая тема »


 




[ Время генерации скрипта: 0.0497 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.