wip, x11 button handling

This commit is contained in:
David Rose 2009-09-09 00:55:23 +00:00
parent d937cb20f9
commit 1513171ae8
8 changed files with 377 additions and 135 deletions

View File

@ -94,3 +94,17 @@ inline FHandle HandleStream::
get_handle() const {
return _buf.get_handle();
}
////////////////////////////////////////////////////////////////////
// Function: HandleStream::has_gdata
// Access: Public
// Description: Returns true if there is data in the stream's "get"
// buffer, meaning that at least one character can be
// extracted from the stream without making an OS read()
// call. Returns false if the get buffer is empty,
// meaning the next read call will hit the OS.
////////////////////////////////////////////////////////////////////
inline bool HandleStream::
has_gdata() const {
return _buf.has_gdata();
}

View File

@ -35,6 +35,7 @@ public:
inline void close_handle();
inline FHandle get_handle() const;
inline bool has_gdata() const;
private:
HandleStreamBuf _buf;

View File

@ -23,3 +23,17 @@ inline FHandle HandleStreamBuf::
get_handle() const {
return _handle;
}
////////////////////////////////////////////////////////////////////
// Function: HandleStreamBuf::has_gdata
// Access: Public
// Description: Returns true if there is data in the stream's "get"
// buffer, meaning that at least one character can be
// extracted from the stream without making an OS read()
// call. Returns false if the get buffer is empty,
// meaning the next read call will hit the OS.
////////////////////////////////////////////////////////////////////
inline bool HandleStreamBuf::
has_gdata() const {
return (egptr() != gptr());
}

View File

@ -38,6 +38,7 @@ public:
void close_handle();
inline FHandle get_handle() const;
inline bool has_gdata() const;
protected:
virtual int overflow(int c);

View File

@ -137,7 +137,7 @@ handle_event(P3D_event_data event) {
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_button_active
// Access: Public
// Access: Public, Virtual
// Description: Sets whether the button should be visible and active
// (true) or invisible and inactive (false). If active,
// the button image will be displayed in the window, and
@ -417,10 +417,12 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
_mouse_y = mouse_y;
_mouse_down = mouse_down;
ButtonState bstate = BS_hidden;
if (!_button_active) {
// The button isn't active, so it's hidden, regardless of the
// mouse position.
_bstate = BS_hidden;
bstate = BS_hidden;
} else {
// Is the mouse pointer within the button region?
bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width &&
@ -435,7 +437,7 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
// and dragging over the button doesn't count.
if (orig_bstate == BS_rollover || _button_depressed) {
_button_depressed = true;
_bstate = BS_click;
bstate = BS_click;
}
} else {
_button_depressed = false;
@ -443,24 +445,25 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
// If we just transitioned from mouse down to mouse up, this
// means a click. And the button automatically hides itself
// after a successful click.
_bstate = BS_hidden;
bstate = BS_hidden;
_button_active = false;
button_click_detected();
} else {
_bstate = BS_rollover;
bstate = BS_rollover;
}
}
} else {
// The mouse is not within the button region. This means ready
// state.
_bstate = BS_ready;
bstate = BS_ready;
if (!_mouse_down) {
_button_depressed = false;
}
}
}
if (orig_bstate != _bstate) {
if (_bstate != bstate) {
_bstate = bstate;
// If we've changed button states, we need to refresh the window.
refresh();
}

View File

@ -57,7 +57,7 @@ public:
virtual bool handle_event(P3D_event_data event);
void set_button_active(bool flag);
virtual void set_button_active(bool flag);
protected:
// This ImageData base class provides minimal functionality for

View File

@ -17,6 +17,7 @@
#ifdef HAVE_X11
#include "get_tinyxml.h"
#include "binaryXml.h"
#include <time.h>
#include <fcntl.h>
#include <sys/wait.h>
@ -47,13 +48,7 @@ P3DX11SplashWindow(P3DInstance *inst) :
// Init for subprocess
_display = None;
_window = None;
_image = NULL;
_resized_image = NULL;
_screen = 0;
_image_width = 0;
_image_height = 0;
_resized_width = 0;
_resized_height = 0;
_graphics_context = None;
_bar_context = None;
_install_progress = 0.0;
@ -101,6 +96,7 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "set_image_filename");
xcommand->SetAttribute("image_filename", image_filename);
xcommand->SetAttribute("image_placement", (int)image_placement);
doc.LinkEndChild(xcommand);
write_xml(_pipe_write, &doc, nout);
@ -150,6 +146,47 @@ set_install_progress(double install_progress) {
check_stopped();
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::set_button_active
// Access: Public, Virtual
// Description: Sets whether the button should be visible and active
// (true) or invisible and inactive (false). If active,
// the button image will be displayed in the window, and
// a click event will be generated when the user clicks
// the button.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
set_button_active(bool flag) {
if (_subprocess_pid == -1) {
return;
}
TiXmlDocument doc;
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "set_button_active");
xcommand->SetAttribute("button_active", (int)flag);
doc.LinkEndChild(xcommand);
write_xml(_pipe_write, &doc, nout);
check_stopped();
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::button_click_detected
// Access: Protected, Virtual
// Description: Called when a button click by the user is detected in
// set_mouse_data(), this method simply turns around and
// notifies the instance. It's a virtual method to give
// subclasses a chance to redirect this message to the
// main thread or process, as necessary.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
button_click_detected() {
// This method is called in the child process, and must relay
// the information to the parent process.
cerr << "click detected\n";
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::start_subprocess
// Access: Private
@ -328,12 +365,9 @@ subprocess_run() {
return;
}
XEvent event;
XSelectInput(_display, _window, ExposureMask | StructureNotifyMask);
string prev_image_filename;
ButtonState prev_bstate = BS_hidden;
string prev_label;
double prev_progress;
double prev_progress = 0.0;
bool needs_redraw = true;
bool needs_draw_label = false;
@ -343,26 +377,52 @@ subprocess_run() {
_subprocess_continue = true;
while (_subprocess_continue) {
// First, scan for X events.
if (XCheckTypedWindowEvent(_display, _window, Expose, &event)
|| XCheckTypedWindowEvent(_display, _window, GraphicsExpose, &event)) {
needs_redraw = true;
}
XEvent event;
while (XCheckWindowEvent(_display, _window, ~0, &event)) {
switch (event.type) {
case Expose:
case GraphicsExpose:
needs_redraw = true;
break;
if (XCheckTypedWindowEvent(_display, _window, ConfigureNotify, &event)) {
if (_resized_image != NULL && (event.xconfigure.width != _win_width ||
event.xconfigure.height != _win_height)) {
XDestroyImage(_resized_image);
_resized_image = NULL;
case ConfigureNotify:
if (event.xconfigure.width != _win_width ||
event.xconfigure.height != _win_height) {
_win_width = event.xconfigure.width;
_win_height = event.xconfigure.height;
// If the window changes size, we need to recompute all of the
// resized images.
_background_image.dump_resized_image();
_button_ready_image.dump_resized_image();
_button_rollover_image.dump_resized_image();
_button_click_image.dump_resized_image();
}
needs_redraw = true;
break;
case MotionNotify:
set_mouse_data(event.xmotion.x, event.xmotion.y, _mouse_down);
break;
case ButtonPress:
set_mouse_data(_mouse_x, _mouse_y, true);
break;
case ButtonRelease:
set_mouse_data(_mouse_x, _mouse_y, false);
break;
}
_win_width = event.xconfigure.width;
_win_height = event.xconfigure.height;
needs_redraw = true;
}
if (_image_filename != prev_image_filename) {
update_image_filename(_image_filename);
update_image(_background_image, needs_redraw);
update_image(_button_ready_image, needs_redraw);
update_image(_button_rollover_image, needs_redraw);
update_image(_button_click_image, needs_redraw);
if (_bstate != prev_bstate) {
needs_redraw = true;
prev_image_filename = _image_filename;
prev_bstate = _bstate;
}
if (_install_label != prev_label) {
@ -431,23 +491,30 @@ subprocess_run() {
// Now check for input from the parent.
int read_fd = _pipe_read.get_handle();
fd_set fds;
FD_ZERO(&fds);
FD_SET(read_fd, &fds);
bool input_ready = _pipe_read.has_gdata();
if (!input_ready) {
int read_fd = _pipe_read.get_handle();
fd_set fds;
FD_ZERO(&fds);
FD_SET(read_fd, &fds);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1; // Sleep a bit to yield the timeslice if there's
// nothing new.
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1; // Sleep a bit to yield the timeslice if there's
// nothing new.
int result = select(read_fd + 1, &fds, NULL, NULL, &tv);
if (result == 1) {
// There is some noise on the pipe, so read it.
int result = select(read_fd + 1, &fds, NULL, NULL, &tv);
if (result > 0) {
// There is some noise on the pipe, so read it.
input_ready = true;
} else if (result == -1) {
// Error in select.
perror("select");
}
}
if (input_ready) {
receive_command();
} else if (result == -1) {
// Error in select.
perror("select");
}
}
@ -476,10 +543,35 @@ receive_command() {
_subprocess_continue = false;
} else if (strcmp(cmd, "set_image_filename") == 0) {
const char *str = xcommand->Attribute("image_filename");
if (str != NULL) {
if (_image_filename != string(str)) {
_image_filename = str;
const string *image_filename = xcommand->Attribute(string("image_filename"));
int image_placement;
if (image_filename != NULL &&
xcommand->QueryIntAttribute("image_placement", &image_placement) == TIXML_SUCCESS) {
X11ImageData *image = NULL;
switch ((ImagePlacement)image_placement) {
case IP_background:
image = &_background_image;
break;
case IP_button_ready:
image = &_button_ready_image;
set_button_range(_button_ready_image);
break;
case IP_button_rollover:
image = &_button_rollover_image;
break;
case IP_button_click:
image = &_button_click_image;
break;
}
if (image != NULL) {
if (image->_filename != *image_filename) {
image->_filename = *image_filename;
image->_filename_changed = true;
}
}
}
@ -496,6 +588,12 @@ receive_command() {
xcommand->Attribute("install_progress", &install_progress);
_install_progress = install_progress;
} else if (strcmp(cmd, "set_button_active") == 0) {
int button_active = 0;
xcommand->Attribute("button_active", &button_active);
P3DSplashWindow::set_button_active(button_active != 0);
}
}
}
@ -508,51 +606,85 @@ receive_command() {
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
redraw() {
if (_image == NULL) {
XClearWindow(_display, _window);
} else {
// We have an image. Let's see how we can output it.
if (_image_width <= _win_width && _image_height <= _win_height) {
// It fits within the window - just draw it.
XPutImage(_display, _window, _graphics_context, _image, 0, 0,
(_win_width - _image_width) / 2, (_win_height - _image_height) / 2,
_image_width, _image_height);
} else if (_resized_image != NULL) {
// We have a resized image already, draw that one.
XPutImage(_display, _window, _graphics_context, _resized_image, 0, 0,
(_win_width - _resized_width) / 2, (_win_height - _resized_height) / 2,
_resized_width, _resized_height);
} else {
// Yuck, the bad case - we need to scale it down.
double scale = min((double) _win_width / (double) _image_width,
(double) _win_height / (double) _image_height);
_resized_width = (int)(_image_width * scale);
_resized_height = (int)(_image_height * scale);
char *new_data = (char*) malloc(4 * _win_width * _win_height);
_resized_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen),
ZPixmap, 0, (char *) new_data, _win_width, _win_height, 32, 0);
double x_ratio = ((double) _image_width) / ((double) _resized_width);
double y_ratio = ((double) _image_height) / ((double) _resized_height);
for (int x = 0; x < _win_width; ++x) {
for (int y = 0; y < _win_height; ++y) {
XPutPixel(_resized_image, x, y,
XGetPixel(_image,
(int)clamp(x * x_ratio, 0, _image_width),
(int)clamp(y * y_ratio, 0, _image_height)));
}
}
XPutImage(_display, _window, _graphics_context, _resized_image, 0, 0,
(_win_width - _resized_width) / 2, (_win_height - _resized_height) / 2,
_resized_width, _resized_height);
XClearWindow(_display, _window);
paint_image(_background_image);
switch (_bstate) {
case BS_hidden:
break;
case BS_ready:
paint_image(_button_ready_image);
break;
case BS_rollover:
if (!paint_image(_button_rollover_image)) {
paint_image(_button_ready_image);
}
break;
case BS_click:
if (!paint_image(_button_click_image)) {
paint_image(_button_ready_image);
}
break;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::paint_image
// Access: Private
// Description: Draws the indicated image, centered within the
// window. Returns true on success, false if the image
// is not defined.
////////////////////////////////////////////////////////////////////
bool P3DX11SplashWindow::
paint_image(X11ImageData &image) {
if (image._image == NULL) {
return false;
}
// We have an image. Let's see how we can output it.
if (image._width <= _win_width && image._height <= _win_height) {
// It fits within the window - just draw it.
XPutImage(_display, _window, _graphics_context, image._image, 0, 0,
(_win_width - image._width) / 2, (_win_height - image._height) / 2,
image._width, image._height);
} else if (image._resized_image != NULL) {
// We have a resized image already, draw that one.
XPutImage(_display, _window, _graphics_context, image._resized_image, 0, 0,
(_win_width - image._resized_width) / 2, (_win_height - image._resized_height) / 2,
image._resized_width, image._resized_height);
} else {
// Yuck, the bad case - we need to scale it down.
double scale = min((double) _win_width / (double) image._width,
(double) _win_height / (double) image._height);
image._resized_width = (int)(image._width * scale);
image._resized_height = (int)(image._height * scale);
char *new_data = (char*) malloc(4 * _win_width * _win_height);
image._resized_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen),
ZPixmap, 0, (char *) new_data, _win_width, _win_height, 32, 0);
double x_ratio = ((double) image._width) / ((double) image._resized_width);
double y_ratio = ((double) image._height) / ((double) image._resized_height);
for (int x = 0; x < _win_width; ++x) {
for (int y = 0; y < _win_height; ++y) {
XPutPixel(image._resized_image, x, y,
XGetPixel(image._image,
(int)clamp(x * x_ratio, 0, image._width),
(int)clamp(y * y_ratio, 0, image._height)));
}
}
XPutImage(_display, _window, _graphics_context, image._resized_image, 0, 0,
(_win_width - image._resized_width) / 2, (_win_height - image._resized_height) / 2,
image._resized_width, image._resized_height);
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::make_window
// Access: Private
// Description: Creates the window for displaying progress. Runs
// within the sub-thread.
// within the sub-process.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
make_window() {
@ -591,7 +723,26 @@ make_window() {
assert(_display != NULL);
assert(parent != None);
_window = XCreateSimpleWindow(_display, parent, x, y, _win_width, _win_height, 0, 0, -1);
int depth = DefaultDepth(_display, _screen);
Visual *dvisual = DefaultVisual(_display, _screen);
long event_mask =
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | StructureNotifyMask;
// Initialize window attributes
XSetWindowAttributes wa;
wa.background_pixel = XWhitePixel(_display, _screen);
wa.border_pixel = 0;
wa.event_mask = event_mask;
unsigned long attrib_mask = CWBackPixel | CWBorderPixel | CWEventMask;
_window = XCreateWindow
(_display, parent, x, y, _win_width, _win_height,
0, depth, InputOutput, dvisual, attrib_mask, &wa);
XMapWindow(_display, _window);
}
@ -642,15 +793,10 @@ setup_gc() {
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
close_window() {
if (_image != NULL) {
XDestroyImage(_image);
_image = NULL;
}
if (_resized_image != NULL) {
XDestroyImage(_resized_image);
_resized_image = NULL;
}
_background_image.dump_image();
_button_ready_image.dump_image();
_button_rollover_image.dump_image();
_button_click_image.dump_image();
if (_bar_context != None) {
if (_bar_context != _graphics_context) {
@ -680,45 +826,41 @@ close_window() {
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::update_image_filename
// Function: P3DX11SplashWindow::update_image
// Access: Private
// Description: Loads the splash image, converts to to bitmap form,
// Description: Loads the splash image, converts to to an XImage,
// and stores it in _image. Runs only in the
// sub-thread.
// child process.
//
// If the image is changed, sets needs_redraw to true.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
update_image_filename(const string &image_filename) {
update_image(X11ImageData &image, bool &needs_redraw) {
if (!image._filename_changed) {
// No changes.
return;
}
image._filename_changed = false;
needs_redraw = true;
// Clear the old image.
if (_image != NULL) {
XDestroyImage(_image);
_image = NULL;
}
if (_resized_image != NULL) {
XDestroyImage(_resized_image);
_resized_image = NULL;
}
image.dump_image();
// Go read the image.
string data;
ImageData image;
if (!read_image_data(image, data, image_filename)) {
if (!read_image_data(image, data, image._filename)) {
return;
}
// Temp legacy support.
_image_width = image._width;
_image_height = image._height;
int num_channels =image._num_channels;
Visual *dvisual = DefaultVisual(_display, _screen);
double r_ratio = dvisual->red_mask / 255.0;
double g_ratio = dvisual->green_mask / 255.0;
double b_ratio = dvisual->blue_mask / 255.0;
uint32_t *new_data = (uint32_t*) malloc(4 * _image_width * _image_height);
uint32_t *new_data = (uint32_t*)malloc(4 * image._width * image._height);
if (num_channels == 3) {
if (image._num_channels == 4) {
int j = 0;
for (int i = 0; i < 3 * _image_width * _image_height; i += 3) {
for (int i = 0; i < 4 * image._width * image._height; i += 4) {
unsigned int r, g, b;
r = (unsigned int)(data[i+0] * r_ratio);
g = (unsigned int)(data[i+1] * g_ratio);
@ -727,13 +869,24 @@ update_image_filename(const string &image_filename) {
(g & dvisual->green_mask) |
(b & dvisual->blue_mask);
}
} else if (num_channels == 1) {
// A grayscale image. Replicate out the channels.
for (int i = 0; i < _image_width * _image_height; ++i) {
} else if (image._num_channels == 3) {
int j = 0;
for (int i = 0; i < 3 * image._width * image._height; i += 3) {
unsigned int r, g, b;
r = (unsigned int)(data[i+0] * r_ratio);
g = (unsigned int)(data[i+1] * g_ratio);
b = (unsigned int)(data[i+2] * b_ratio);
new_data[j++] = (r & dvisual->red_mask) |
(g & dvisual->green_mask) |
(b & dvisual->blue_mask);
}
} else if (image._num_channels == 1) {
// A grayscale image. Replicate out the channels.
for (int i = 0; i < image._width * image._height; ++i) {
unsigned int r, g, b;
r = (unsigned int)(data[i] * r_ratio);
g = (unsigned int)(data[i] * g_ratio);
b = (unsigned int)(data[i] * b_ratio);
new_data[i] = (r & dvisual->red_mask) |
(g & dvisual->green_mask) |
(b & dvisual->blue_mask);
@ -741,8 +894,39 @@ update_image_filename(const string &image_filename) {
}
// Now load the image.
_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen),
ZPixmap, 0, (char *) new_data, _image_width, _image_height, 32, 0);
image._image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen),
ZPixmap, 0, (char *)new_data, image._width, image._height, 32, 0);
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::X11ImageData::dump_image
// Access: Public
// Description: Frees the previous image data.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::X11ImageData::
dump_image() {
if (_image != NULL) {
XDestroyImage(_image);
_image = NULL;
}
if (_resized_image != NULL) {
XDestroyImage(_resized_image);
_resized_image = NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DX11SplashWindow::X11ImageData::dump_resized_image
// Access: Public
// Description: Frees the previous resized image data only, retaining
// the original image data.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::X11ImageData::
dump_resized_image() {
if (_resized_image != NULL) {
XDestroyImage(_resized_image);
_resized_image = NULL;
}
}
#endif // HAVE_X11

View File

@ -41,6 +41,11 @@ public:
virtual void set_install_label(const string &install_label);
virtual void set_install_progress(double install_progress);
virtual void set_button_active(bool flag);
protected:
virtual void button_click_detected();
private:
void start_subprocess();
void stop_subprocess();
@ -53,22 +58,44 @@ private:
private:
// These methods run only within the subprocess.
class X11ImageData;
void subprocess_run();
void receive_command();
void redraw();
bool paint_image(X11ImageData &image);
void make_window();
void setup_gc();
void update_image_filename(const string &image_filename);
void update_image(X11ImageData &image, bool &needs_redraw);
void close_window();
private:
// Data members that are stored in the subprocess.
class X11ImageData : public ImageData {
public:
inline X11ImageData();
inline ~X11ImageData();
void dump_image();
void dump_resized_image();
string _filename;
bool _filename_changed;
XImage *_image;
XImage *_resized_image;
int _resized_width, _resized_height;
};
X11ImageData _background_image;
X11ImageData _button_ready_image;
X11ImageData _button_rollover_image;
X11ImageData _button_click_image;
bool _subprocess_continue;
HandleStream _pipe_read;
bool _own_display;
string _image_filename;
string _install_label;
double _install_progress;
@ -79,14 +106,12 @@ private:
GC _graphics_context;
GC _bar_context;
unsigned long _blue_pixel;
XImage* _image;
XImage* _resized_image;
int _image_width, _image_height;
int _resized_width, _resized_height;
Window _window;
};
#include "p3dX11SplashWindow.I"
#endif // HAVE_X11
#endif