Your Digital Media Has Never Looked So Good

 
buchanan
Topic Author
Posts: 29
Joined: Fri Nov 10, 2006 2:43 pm
Location: Wisconsin USA

Java program for SB remote control and display

Sat Nov 18, 2006 6:28 pm

Hi Roku fans -

Here's some java code I wrote to drive SBs from my computer. It
does not try to do anything beyond emulate the SB display and
remote control. It works pretty well for me operating two M1000s
(running software version 2.5.171)
from Linux and MacOSX boxes under Java 1.4.2 and 1.5. I tried to
write it to handle M2000 but don't know if it works because I don't
have one on which to test. It probably won't be terribly useful on
M500 until RCP supports GetDisplayData on it (and the code is
modified to use it). No promises, warranties, etc, etc, but here
it is...

Alan

import        java.awt.*;
import        java.awt.geom.*;
import        java.awt.image.*;
import        java.awt.event.*;
import        java.io.*;
import        java.net.*;
import        java.util.*;
import        java.util.prefs.*;
import        javax.swing.*;
import        javax.swing.border.*;

/**
Virtual remote control and screen display for Roku SoundBridge.
Should work for M1000/1 and maybe M2000, probably not M500.
Copyright 2006 R.A.Buchanan. Free for private noncommercial use.
*/
public class SoundBridge
{
/** SoundBridge screen. */
private static class Display extends JPanel
{
/** Blits simulated SB pixels onto black background. */
public void paintComponent( Graphics g )
        {
        g.setColor( Color.black );
        g.fillRect( 0, 0, getWidth(), getHeight() );
        Insets in = getInsets();
        for( int i = 0; data_!=null && i < data_.length*8 && i < size_.width*size_.height; i++ )
                if( ( data_[i/8] & 128>>(i%8) ) > 0 )
                        g.drawImage( pixel_, in.left + 2*(i/size_.height), in.top + 3*(i%size_.height), null );
        }       
/** Scales so each SB pixel appears as 2x3 screen pixels. */
public Dimension getPreferredSize()
        {
        Insets in = getInsets();
        return new Dimension( size_.width*2+1 + (in.left+in.right), size_.height*3+1 + (in.top+in.bottom) );
        }
/** Called by Connection when screen content changes. */
public void setData( String count, String hex )
        {
        if( data_ == null )
                try        {
                        int n = Integer.parseInt( count );
                        size_ = new Dimension( n / ( n==560 ? 2 : 4 ), n==560 ? 16 : 32 );
                        data_ = new byte[ Math.min( n, hex.length()/2 ) ];
                        }
                catch( Exception ex )        { return; }
        for( int i = 0; i < Math.min( data_.length, hex.length()/2 ); i++ )
                try        { data_[i] = (byte)Integer.parseInt( hex.substring( 2*i, 2*i+2 ), 16 ); }
                catch( Exception ex )        {}
        repaint();
        }
/** Will be replaced by actual SB dimensions when data arrives. Assumes M1000 size until connected. */
private Dimension size_ = new Dimension( 280, 16 );
/** Contents of SB screen, L to R, T to B, (high order bit first) as returned by GetDisplayData. */
private byte[] data_ = null;
/** 3x4 image representing one SB screen pixel. To be overlapped by 1 in each direction. */
private static BufferedImage pixel_;
static        {
        int[] rgb = {
                0x1E7878, 0x289F9F, 0x1E7878,
                0x30BFBF, 0x40FFFF, 0x30BFBF,
                0x30BFBF, 0x40FFFF, 0x30BFBF,
                0x1E7878, 0x289F9F, 0x1E7878 };
        pixel_ = new BufferedImage( 3, 4, BufferedImage.TYPE_INT_RGB );
        pixel_.setRGB( 0, 0, 3, 4, rgb, 0, 3 );
        }
}

/** SoundBridge I/O over TCP socket 5555. */
private class Connection
{
/** Initiate a connection. Retries until success or after failure. */
public Connection()
        { timer_.schedule( new ConnectTask(), 0, 1000 ); }
/** Repeatable attempt to initiate a connection. */
private class ConnectTask extends TimerTask { public void run()
        {
        try        {
                if( socket_ != null )
                        try        { socket_.close(); }
                        catch( Exception ex )        {}
                String host = preferences_.get( "hostname_or_address", preferences_.name() );
                socket_ = new Socket( host, 5555 );
                preferences_.put( "hostname_or_address", host );
                ( new Thread() { public void run()
                        {
                        final TimerTask updateTask = new TimerTask() { public void run()
                                { // periodic task to request SB display data after change notification.
                                if( updated_ )
                                        send( "GetDisplayData" );
                                updated_ = false;       
                                } };
                        try        {
                                writer_ = new PrintWriter( socket_.getOutputStream(), true );
                                writer_.println( "GetDisplayData" ); // initial request
                                writer_.println( "DisplayUpdateEventSubscribe" ); // listen for changes
                                BufferedReader reader = new BufferedReader( new InputStreamReader( socket_.getInputStream() ) );
                                timer_.schedule( updateTask, 200, 200  );
                                for( String l = reader.readLine(); l != null; l = reader.readLine() )
                                        if( l.startsWith( "DisplayUpdateEvent" ) )
                                                updated_ = true;
                                        else
                                        if( l.startsWith( "GetDisplayData: data bytes: " ) )
                                                display_.setData( l.substring( "GetDisplayData: data bytes: ".length() ), reader.readLine() );
                                }
                        catch( Exception ex )
                                {
                                updateTask.cancel();
                                timer_.schedule( new ConnectTask(), 1000, 1000 );
                                }
                        } } ).start();
                }
        catch( Exception ex )        { return; }
        cancel();
        } }
/** Transmit a control message to the SB. */
public synchronized void send( String command )
        {
        try        { writer_.println( command ); }
        catch( Exception ex )
                {
                try        { socket_.close(); }
                catch( Exception exx )        {}
                } // will cause read failure and restart
        }
/** TCP socket to SB port 5555. */
private Socket socket_;
/** Text output to SB. */
private PrintWriter writer_;
/** True if DisplayUpdateEvent has arrived since last GetDisplayData. */
private boolean updated_ = false;
}

/** Remote control button. */
private class RemoteButton extends JButton
{
/** Button associated with one remote control command. */
public RemoteButton( int index )
        {
        super( icon_[index] );
        boolean[] v = { command_[index].equals( "VOLUME_UP" ), command_[index].equals( "VOLUME_DOWN" ) };
        setBorder( new EmptyBorder( v[1]?0:8, 16, v[0]?0:8, 16 ) );
        setMargin( new Insets( v[1]?-12:4, 4, v[0]?-12:4, 4 ) );
        setOpaque( false );
        setToolTipText( command_[index] );
        final String command = "IrDispatchCommand CK_" + command_[index];
        final javax.swing.Timer repeat = new javax.swing.Timer( 110, new ActionListener()
                { public void actionPerformed( ActionEvent ae ) { connection_.send( command ); } } );
        repeat.setInitialDelay( 600 );
        addMouseListener( new MouseAdapter()
                {
                public void mousePressed( MouseEvent me )
                        {
                        connection_.send( command );
                        repeat.start();
                        }
                public void mouseReleased( MouseEvent me )
                        { repeat.stop(); }
                } );
        }
/** Paints a gray rounded rectangle similar in appearance to SB remote control hardware buttons. */
public void paintComponent( Graphics g )
        {
        Insets m = getMargin();
        g.setColor( gray_[ getModel().isPressed() ? 0 : 1 ] );
        ((Graphics2D)g).setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        ((Graphics2D)g).fill( new RoundRectangle2D.Double( m.left, m.top, getWidth()-(m.left+m.right), getHeight()-(m.top+m.bottom), 8, 8 ) );
        Icon i = getIcon(); // draw icon directly because some platforms don't handle transparent icon BG correctly
        i.paintIcon( this, g, (getWidth()-i.getIconWidth())/2, m.top<0 ? 0 : ( (getHeight()-i.getIconHeight()) / ( m.bottom<0 ? 1 : 2 ) ) );
        }
/** Normal/pressed background colors. */
private Color[] gray_ = { new Color( 0x484848 ), new Color( 0x282828 ) };
}

/** Set up display window, socket connection, remote control window, and configuration support for one SB. */
private SoundBridge( Preferences preferences )
        {
        preferences_ = preferences;
        display_ = new Display();
        display_.setBorder( new EmptyBorder( 8, 8, 8, 8 ) );
        connection_ = new Connection();
       
        String host = preferences_.get( "hostname_or_address", preferences_.name() );
        final JFrame frame = new JFrame( "Roku SoundBridge: " + host );

        final JDialog remoteDialog = new JDialog( frame, host );
        JComponent remote = new JPanel( new GridLayout( 0, 3 ) );
        remote.setBackground( Color.black );
        remote.setBorder( new EmptyBorder( 4, 4, 4, 4 ) );
        for( int i = 0; i < command_.length; i++ )
                remote.add( command_[i]!=null ? (Component)( new RemoteButton(i) ) : ( new Component() {} ) );
        remoteDialog.getContentPane().add( remote );
        remoteDialog.setResizable( false );
        remoteDialog.pack();
               
        final JDialog configDialog = new JDialog( frame, host + ": Host Name or IP Address" );
        final JTextField text = new JTextField( preferences_.get( "hostname_or_address", "" ), 32 );
        text.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ae )
                {
                preferences_.put( "hostname_or_address", text.getText() );
                frame.setTitle( "Roku SoundBridge: " + text.getText() );
                remoteDialog.setTitle( text.getText() );
                configDialog.setTitle( text.getText() + ": Host Name or IP Address" );
                configDialog.setVisible( false );
                } } );
        configDialog.getContentPane().add( text );
        configDialog.setResizable( false );
        configDialog.pack();

        final JPopupMenu menu = new JPopupMenu();
        JMenuItem mi;
        menu.add( mi = new JMenuItem( "Show Remote Control" ) );
        mi.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ae )
                {
                remoteDialog.setVisible( true );
                remoteDialog.setLocation( new Point( frame.getX(), frame.getY()+frame.getHeight() ) );
                } } );
        menu.add( mi = new JMenuItem( "Set SoundBridge Hostname or IP Address" ) );
        mi.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ae )
                {
                configDialog.setVisible( true );
                configDialog.setLocation( new Point( frame.getX()+remoteDialog.getWidth(), frame.getY()+frame.getHeight() ) );
                } } );
        menu.add( mi = new JMenuItem( "Add Another SoundBridge" ) );
        mi.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ae )
                {
                try        {
                        java.util.List hosts = Arrays.asList( preferences_.parent().childrenNames() );
                        for( int i = 0; i < hosts.size()+1; i++ )
                                if( !hosts.contains( "" + i ) )
                                        {
                                        new SoundBridge( preferences_.parent().node( "" + i ) );
                                        return;
                                        }
                        }
                catch( Exception ex )        {}
                } } );
        menu.add( mi = new JMenuItem( "Forget This SoundBridge" ) );
        mi.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent ae )
                {
                frame.setVisible( false );
                try        { preferences_.removeNode(); }
                catch( Exception ex )        {}
                } } );
        display_.addMouseListener( new MouseAdapter() { public void mousePressed( MouseEvent me )
                { menu.show( display_, me.getX(), me.getY() ); } } );
       
        frame.setResizable( false );
        frame.addComponentListener( new ComponentAdapter()
                {
                public void componentMoved( ComponentEvent ce )
                        {
                        preferences_.putInt( "x", frame.getX() );
                        preferences_.putInt( "y", frame.getY() );
                        }
                public void componentHidden( ComponentEvent ce )
                        {
                        if( --instances_ == 0 ) // to do: shut down connection, tasks, etc
                                System.exit(0);
                        }
                } );
        frame.setContentPane( display_ );
        frame.pack();
        frame.setLocation( new Point( preferences_.getInt( "x", (instances_%2)*600 ), preferences_.getInt( "y", (instances_/2)*200 ) ) );
        frame.setVisible( true );
        remoteDialog.setLocation( new Point( frame.getX(), frame.getY()+frame.getHeight() ) );
        remoteDialog.setVisible( true );
        configDialog.setLocation( new Point( frame.getX()+remoteDialog.getWidth(), frame.getY()+frame.getHeight() ) );
        if( "".equals( preferences_.get( "hostname_or_address", "" ) ) )
                configDialog.setVisible( true );
        instances_++;
        }
       
/** Configuration of host/IP, screen placement, etc. */
private final Preferences preferences_;
/** SB screen display panel. Updated by Connection object. */
private final Display display_;
/** SoundBridge I/O. */
private final Connection connection_;

/** Shared timer for connection retries, update throttling, etc. */
private static java.util.Timer timer_ = new java.util.Timer( true );
/** Instance count so process can exit when last display window is closed. */
private static int instances_ = 0;

/** SoundBridge command codes (prefixed w/ "CK_"). */
private static String[] command_ = {
        "POWER",     null,         "BRIGHTNESS",
        "MENU",     "SEARCH",      "EXIT",
         null,      "NORTH",        null,
        "WEST",     "SELECT",      "EAST",
         null,      "SOUTH",        null,
        "PREVIOUS", "PLAY",        "NEXT",
        "PAUSE",    "VOLUME_UP",   "SHUFFLE",
        "ADD",      "VOLUME_DOWN", "REPEAT" };
/** SoundBridge control icons. */
private static Icon[] icon_ = new Icon[24];
static        {
        int size[][] = { // dimensions
                { 12, 13 }, {        }, { 13, 13 },
                { 12, 12 }, { 13, 13 }, { 16, 11 },
                {        }, { 11,  6 }, {        },
                {  6, 11 }, { 12, 13 }, {  6, 11 },
                {        }, { 11,  6 }, {        },
                { 16, 11 }, {  6, 11 }, { 16, 11 },
                {  6, 11 }, { 18, 21 }, { 18, 13 },
                { 10, 10 }, { 18, 21 }, { 18, 13 } };
        String[] hex = { // pixels
                "0600060036c076e06660c630c030c030c030606070e03fc00f00", null, "020042102020070008801040d258104008800700202042100200",
                "06000f001f803fc07fe0fff0f0f070e070e070e070e070e0", "03e007f00e380c180c180c180e3807f01be038007000e0004000",
                "0078007e00070003000320036007fffefff860002000",
                 null, "04000e001f003f807fc0ffe0",null,
                "040c1c3c7cfc7c3c1c0c04", "00100030006000e000c021c07380ff807f003f001e000e000400", "80c0e0f0f8fcf8f0e0c080",
                 null, "ffe07fc03f801f000e000400", null,
                "c081c183c387c78fcf9fdfbfcf9fc78fc387c183c081", "80c0e0f0f8fcf8f0e0c080", "8103c183e1c3f1e3f9f3fdfbf9f3f1e3e1c3c1838103",
                "cccccccccccccccccccccc",
                "00400000e00001f00003b800071c000e0e000404000000000000000000000000000000000000000001000009000208800604800e24401e2240fe1240fe1240",
                "000100000180f80fc0fe3fc006718000e10001c000038100073180fe3fc0f80fc0000180000100",
                "0c000c000c000c00ffc0ffc00c000c000c000c00",
                "fe1240fe12401e22400e24400604800208800009000001000000000000000000000000000000000000000404000e0e00071c0003b80001f00000e000004000",
                "0400000600001f7e007f7f80e601c0c400c0c000c0c008c0e019c07fbf801fbe00001800000800" };
        for( int i = 0; i < 24; i++ )
                if( command_[i] != null )
                        { // TYPE_INT_ARGB instead of TYPE_BYTE_BINARY due to transparency bugs in some J2D implmentations
                        BufferedImage image = new BufferedImage( size[i][0], size[i][1], BufferedImage.TYPE_INT_ARGB );
                        int stride = 8 * (int)Math.ceil( image.getWidth() / 8. );
                        byte b;
                        for( int j = 0; j < hex[i].length()/2; j++ )
                                if( ( b = (byte)Integer.parseInt( hex[i].substring( 2*j, 2*j+2 ), 16 ) ) != 0 )
                                        for( int k = 0; k < 8; k++ )
                                                if( ( b & (128>>k) ) > 0 )
                                                        image.setRGB( (8*j+k) % stride, (8*j+k) / stride, -1 );
                        icon_[i] = new ImageIcon( image );
                        }
        }

/** Main launch point. SB host/IP(s) may be provided as arguments. */
public static void main( String[] argv )
        {
        Preferences preferences = Preferences.userNodeForPackage( SoundBridge.class );
        Set hosts = new TreeSet( Arrays.asList( argv ) ); // first check for SB hosts explicitly passed via argv
        if( hosts.isEmpty() ) // then check for remembered SBs
                try        { hosts.addAll( Arrays.asList( preferences.childrenNames() ) ); }
                catch( Exception ex )
                        { ex.printStackTrace( System.out ); }
       
        if( hosts.isEmpty() ) // to do: could add service discovery here
                hosts.add( "0" ); // one unconnected display if nothing else
        for( Iterator i = hosts.iterator(); i.hasNext(); )
                new SoundBridge( preferences.node( (String)i.next() ) );
        }
}
 
buchanan
Topic Author
Posts: 29
Joined: Fri Nov 10, 2006 2:43 pm
Location: Wisconsin USA

Mon Nov 20, 2006 8:13 am

blamm wrote:
How do I use this?


To build and run you need:

1) The Java SE Development Kit (JDK) for your platform. I would use 5.0.
You can get this from http://java.sun.com for Windows or Linux; for Mac
you get it from Apple.

2) The IP address of your SoundBridge (the same one you'd use to connect
via telnet). You can get this under "System Configuration" on the SB.

This is a single stand-alone java class. There are no external dependencies.
To build:

1) Save the code in a file named "SoundBridge.java"

2) Compile with "javac SoundBridge.java". This should produce a file named
"SoundBridge.class". This file contains the executable java bytecodes.

To run:

1) Run the program with "java SoundBridge <ip_address>". You should get two
windows, one approximating the SB screen, the other the remote control. If
you get this far the standard SB instructions apply; the program has all the
functionality (and all the limitations!) of the physical display and remote.

As I said in the original post, I think it will work for the M1000/1, and
it may work for the M2000, but the M500 does not as yet supply the screen
data it needs.
 
frethop
Posts: 81
Joined: Tue Nov 29, 2005 6:18 am

Thu Dec 07, 2006 6:31 am

SBC is already written for the SoundBridge -- and is Java-based. I have a new version coming soon. In addition, I have another application based in Java for the SoundBridge that will be out in the next weeks.

I am always open to sharing effort on building a base of Java classes for the SoundBridge.

-F
 
mcgarry
Posts: 4
Joined: Sat May 27, 2006 5:39 pm

Sun Dec 31, 2006 7:22 pm

awsome piece of code! slightly modified to include SNOOZE and ALARM commands makes this java-based remote control to a must-have.

thanks and cheers,
marc
 
S80_UK
Posts: 1035
Joined: Wed Oct 25, 2006 3:11 am
Location: Cambridgeshire, UK

Mon Jan 01, 2007 8:19 am

I just compiled this code - I have to agree - AWESOME!

Very simple and highly effective. Sure beats running down stairs to check things. Does exactly what I want (I am running an M1000).

Well done, buchanan
 
usbrit
Posts: 5
Joined: Sun Nov 26, 2006 6:34 pm

Sun Jan 07, 2007 8:54 pm

awsome piece of code! slightly modified to include SNOOZE and ALARM commands makes this java-based remote control to a must-have.

Does anyone have the code including the commands for alarm and snooze?
This app works great for everything else.
Thanks
usbrit
 
buchanan
Topic Author
Posts: 29
Joined: Fri Nov 10, 2006 2:43 pm
Location: Wisconsin USA

Tue Jan 09, 2007 9:56 am

The compile actually builds several classes in addition to SoundBridge.class. When you run directly from the directory in which you built, they're all visible at runtime. If you link the main class and run from elsewhere, the main class gets found but not the others. There are a number of ways to package a java program to avoid this. One way is to make a .bat file containing:
java -classpath C:\RokuStuff SoundBridge

where C:\RokuStuff is the path to the directory where you built the class files. You should then be able to link to the .bat file from anywhere.

The "real" way to do it is to put the classes into a .jar (java archive) file with a Main-Class tag, but that's a bit complicated to explain here. Another packaging possibility would be to run inside a web browser. That would require modifying the code to run as applet(s), and setting security permissions to allow the socket connections.
 
S80_UK
Posts: 1035
Joined: Wed Oct 25, 2006 3:11 am
Location: Cambridgeshire, UK

Tue Jan 09, 2007 2:59 pm

buchanan wrote:
One way is to make a .bat file containing:
java -classpath C:\RokuStuff SoundBridge

where C:\RokuStuff is the path to the directory where you built the class files. You should then be able to link to the .bat file from anywhere.

And when you've got the batch file done, you can create a shortcut to it, and assign an icon to it such as this one (better than the default MS offering for batch files IMHO)...

http://www.wizard-widgets.co.uk/roku/roku-icon.zip - here to download the icon

http://www.wizard-widgets.co.uk/roku/roku.ico - here to view

Cheers!
 
usbrit
Posts: 5
Joined: Sun Nov 26, 2006 6:34 pm

Mon Jan 15, 2007 8:44 am

String[] hex = { // pixels
"0600060036c076e06660c630c030c030c030606070e03fc00f00", null,

buchanan,
I figured out how to add the button for alarm command and recompile, using a "last" button as the icon. Do you have a hex icon for "alarm" or how do I figure out the hex for that?
Thanks
usbrit
 
buchanan
Topic Author
Posts: 29
Joined: Fri Nov 10, 2006 2:43 pm
Location: Wisconsin USA

Mon Jan 15, 2007 1:28 pm

blamm wrote:
Hi.

This is a great interface...I love it. Unfortunatley I get three instances everytime I launch it. Any Idea why?


It keeps track of windows in a preferences object, so any window you create returns when you restart. You should be able to remove the extras by clicking on each unwanted display window and selecting "Forget This SoundBridge" from the menu.
 
buchanan
Topic Author
Posts: 29
Joined: Fri Nov 10, 2006 2:43 pm
Location: Wisconsin USA

Mon Jan 15, 2007 1:42 pm

usbrit wrote:
String[] hex = { // pixels
"0600060036c076e06660c630c030c030c030606070e03fc00f00", null,

buchanan,
I figured out how to add the button for alarm command and recompile, using a "last" button as the icon. Do you have a hex icon for "alarm" or how do I figure out the hex for that?
Thanks
usbrit


The code is written with embedded hex graphics primarily because I wanted to post it as standalone text in the forum. In real life a more sensible way to do icons would be to replace the static block that initializes the icons with something like
private static Icon[] icon_ = {
   new ImageIcon( "power.png" ),
   null,
   new ImageIcon( "brightness.png" ),
   new ImageIcon( "menu.png" ),
   new ImageIcon( "search.png" ),
   ...
   new ImageIcon( "repeat.png" ) };

Where "power.png", etc are the names of graphics files in the same directory as the program. Then you can create and edit them with the tool of your choice to make them incredibly beautiful. I like PNG because it has good support for transparency, antialiasing, etc, but ImageIcon does support several other formats.
 
usbrit
Posts: 5
Joined: Sun Nov 26, 2006 6:34 pm

Mon Jan 15, 2007 9:45 pm

buchanan,
Thanks for the answer, however, your existing code works great and now all I really need is the hex for a Bell or alarm type icon. I would prefer not to rewrite the code and create all the png's. I can't find any software to do the convert from a bmp. Any ideas?
Thanks
usbrit
 
S80_UK
Posts: 1035
Joined: Wed Oct 25, 2006 3:11 am
Location: Cambridgeshire, UK

Tue Jan 16, 2007 3:02 am

usbrit,
If you are only adding a couple of small icons, you can easily work these out on a sheet of squared paper. Define a grid - maybe 16 by 16 (but larger or smaller is possible) - and draw your icon. Then using the info below you should be able to add in the code for your new icons.

In the original source code, this table defines the active (white) area for each icon (x and y) in pixels...

int size[][] = { // dimensions
{ 12, 13 }, { }, { 13, 13 },
{ 12, 12 }, { 13, 13 }, { 16, 11 },
{ }, { 11, 6 }, { },
{ 6, 11 }, { 12, 13 }, { 6, 11 },
{ }, { 11, 6 }, { },
{ 16, 11 }, { 6, 11 }, { 16, 11 },
{ 6, 11 }, { 18, 21 }, { 18, 13 },
{ 10, 10 }, { 18, 21 }, { 18, 13 } };


Then come the icons themselves.

So, the line of hex
"0078007e00070003000320036007fffefff860002000",
is the "Back" symbol on the remote - That is the sixth icon and is defined as 16 pixels wide by 11 pixels high in the table.

And the line of hex
"040c1c3c7cfc7c3c1c0c04",
is the left pointer symbol. That is the 22nd icon defined in the table and is 6 pixels wide by 11 pixels high.

From these two examples, you can see that you only define as much as you need, and the hex strings are either groups of 8 or 16 or 24 pixels defined left to right, top to bottom, so if your icon is between 9 and 16 pixels wide, you define it with one group of four hex digits for each line of pixels that is needed. In the table you insert the x and y sizes needed.

Hope this is useful to you. Good luck!
 
psycho13
Posts: 4
Joined: Wed Jan 31, 2007 3:28 pm

Thu Feb 08, 2007 3:54 pm

Do you mean something like an exe file which you can run without compiling the code? Problem is that Java needs a virtual machine to work. But perhaps there is some sort of launcher which can make the pre-compiled class run without any problems just having the Java-VM installed.

But for that issue a batch file does the job, too...
 
beeka
Posts: 54
Joined: Sat Feb 10, 2007 6:19 am
Location: Portsmouth, UK

Tue Feb 13, 2007 4:46 pm

psycho13 wrote:
Do you mean something like an exe file which you can run without compiling the code? Problem is that Java needs a virtual machine to work. But perhaps there is some sort of launcher which can make the pre-compiled class run without any problems just having the Java-VM installed.


Launch4j is such a launcher, which I have used to wrap the application. You still need the Java Runtime Environment (JRE), but does mean you won't have to know how to compile Java :-) to use the remote. You can see what I did to make the executable for Windows and download it from here.

Enjoy,

Steve.

Who is online

Users browsing this forum: No registered users and 1 guest