diff options
author | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-19 19:35:20 +0000 |
---|---|---|
committer | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-19 19:35:20 +0000 |
commit | 5efe44c3aefe5ca73a9c3edbdaf0cf51ba72294a (patch) | |
tree | b81c8ba11ef6fe41a5e7840f6ba8e6f009e86e56 /native_client_sdk | |
parent | 7f2c1921a1b70eeaf3e62c25dc6f72b308259080 (diff) | |
download | chromium_src-5efe44c3aefe5ca73a9c3edbdaf0cf51ba72294a.zip chromium_src-5efe44c3aefe5ca73a9c3edbdaf0cf51ba72294a.tar.gz chromium_src-5efe44c3aefe5ca73a9c3edbdaf0cf51ba72294a.tar.bz2 |
Add simple NaCl gamepad example
BUG=79098
Review URL: http://codereview.chromium.org/9148086
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118334 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r-- | native_client_sdk/src/examples/gamepad/Makefile | 183 | ||||
-rw-r--r-- | native_client_sdk/src/examples/gamepad/gamepad.cc | 149 | ||||
-rw-r--r-- | native_client_sdk/src/examples/gamepad/gamepad.h | 80 | ||||
-rw-r--r-- | native_client_sdk/src/examples/gamepad/gamepad.html | 24 | ||||
-rw-r--r-- | native_client_sdk/src/examples/gamepad/gamepad.nmf | 6 | ||||
-rw-r--r-- | native_client_sdk/src/examples/gamepad/gamepad_module.cc | 34 |
6 files changed, 476 insertions, 0 deletions
diff --git a/native_client_sdk/src/examples/gamepad/Makefile b/native_client_sdk/src/examples/gamepad/Makefile new file mode 100644 index 0000000..f1a69f1 --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/Makefile @@ -0,0 +1,183 @@ +# Copyright (c) 2012 The Native Client Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# +# GNU Make based build file. For details on GNU Make see: +# http://www.gnu.org/software/make/manual/make.html +# + +# +# Project information +# +# These variables store project specific settings for the project name +# build flags, files to copy or install. In the examples it is typically +# only the list of sources and project name that will actually change and +# the rest of the makefile is boilerplate for defining build rules. +# +PROJECT:=gamepad +CXX_SOURCES:=gamepad.cc gamepad_module.cc +COPY_FILES:=gamepad.html gamepad.nmf +LDFLAGS:=-lppapi_cpp -lppapi + + +# +# Get pepper directory for toolchain and includes. +# +# If PEPPER_ROOT is not set, then assume it can be found a two directories up, +# from the default example directory location. +# +THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST))) +PEPPER_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..) + +# Project Build flags +DEFINES:= +INCLUDES:= +WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror +CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS) $(DEFINES) $(INCLUDES) + +# +# Compute tool paths +# +# +OSNAME:=$(shell python $(PEPPER_ROOT)/tools/getos.py) +TC_PATH:=$(abspath $(PEPPER_ROOT)/toolchain/$(OSNAME)_x86_newlib) +CC:=$(TC_PATH)/bin/i686-nacl-gcc +CXX:=$(TC_PATH)/bin/i686-nacl-g++ +STRIP:=$(TC_PATH)/bin/i686-nacl-strip + +# +# Create shell aliases +# +# Create Python based aliases for common shell commands like copy or move. +# +COPY = python $(PEPPER_ROOT)/tools/oshelpers.py cp +MKDIR = python $(PEPPER_ROOT)/tools/oshelpers.py mkdir +RM = python $(PEPPER_ROOT)/tools/oshelpers.py rm +MV = python $(PEPPER_ROOT)/tools/oshelpers.py mv + +# +# Disable DOS PATH warning when using Cygwin based tools Windows +# +CYGWIN ?= nodosfilewarning +export CYGWIN + +# +# Define a macro for copying files to the configuration directory +# +# Copys a source file to the destination directory, removing the base path +# from the source. Adds a dependency to the destination directory in case it +# needs to be created. +# +# $(1) = Source file +# $(2) = Destination directory +define FILE_COPY +$(2)/$(notdir $(1)) : $(1) | $(2) + $(COPY) $(1) $(2) +$(2)_COPIES+=$(2)/$(notdir $(1)) +endef + + +# Declare the ALL target first, to make the 'all' target the default build +all: DEBUG RELEASE + + +# +# Debug Build rules. +# +DEBUG_x86_32_FLAGS:=-m32 -O0 -g +DEBUG_x86_64_FLAGS:=-m64 -O0 -g +DEBUG_x86_32_OBJS:=$(patsubst %.cc,DBG/x86_32/%.o,$(CXX_SOURCES)) +DEBUG_x86_64_OBJS:=$(patsubst %.cc,DBG/x86_64/%.o,$(CXX_SOURCES)) + +# Create DBG configuration directories +DBG: + $(MKDIR) -p $@ + +DBG/x86_32: + $(MKDIR) -p $@ + +DBG/x86_64: + $(MKDIR) -p $@ + +# Copy all files to that config +$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),DBG))) + +# Include generated dependencies +-include DBG/x86_32/*.d +-include DBG/x86_64/*.d + +# Define compile rule for all 32 bit debug objects +DBG/x86_32/%.o : %.cc $(THIS_MAKE) | DBG/x86_32 + $(CXX) -o $@ -c $< $(DEBUG_x86_32_FLAGS) $(CXXFLAGS) -MMD -MF $@.d + +# Define compile rule for all 64 bit debug objects +DBG/x86_64/%.o : %.cc $(THIS_MAKE) | DBG/x86_64 + $(CXX) -o $@ -c $< $(DEBUG_x86_64_FLAGS) $(CXXFLAGS) + +# Define Link rule for 32 bit debug NEXE +DBG/$(PROJECT)_x86_32.nexe : $(DEBUG_x86_32_OBJS) + $(CXX) -o $@ $^ $(DEBUG_x86_32_FLAGS) $(LDFLAGS) + +# Define Link rule for 64 bit debug NEXE +DBG/$(PROJECT)_x86_64.nexe : $(DEBUG_x86_64_OBJS) + $(CXX) -o $@ $^ $(DEBUG_x86_64_FLAGS) $(LDFLAGS) + +# Define a DEBUG alias to build the debug version +.PHONY : DEBUG RUN_DEBUG +DEBUG : DBG/$(PROJECT)_x86_32.nexe DBG/$(PROJECT)_x86_64.nexe $(DBG_COPIES) + +# Define a RUN_DEBUG alias to build and server the DEBUG version +RUN_DEBUG: DEBUG + cd DBG && python ../../httpd.py + + +# +# Release build rules. +# +RELEASE_x86_32_FLAGS:=-m32 -O2 -g +RELEASE_x86_64_FLAGS:=-m64 -O2 -g +RELEASE_x86_32_OBJS:=$(patsubst %.cc,REL/x86_32/%.o,$(CXX_SOURCES)) +RELEASE_x86_64_OBJS:=$(patsubst %.cc,REL/x86_64/%.o,$(CXX_SOURCES)) + +REL: + $(MKDIR) -p $@ + +REL/x86_32: + $(MKDIR) -p $@ + +REL/x86_64: + $(MKDIR) -p $@ + +# Copy all files to that config +$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),REL))) + +# Include generated dependencies +-include REL/x86_32/*.d +-include REL/x86_64/*.d + +# Define compile rule for all 32 bit debug objects +REL/x86_32/%.o : %.cc $(THIS_MAKE) | REL/x86_32 + $(CXX) -o $@ -c $< $(RELEASE_x86_32_FLAGS) $(CXXFLAGS) -MMD -MF $@.d + +# Define compile rule for all 64 bit debug objects +REL/x86_64/%.o : %.cc $(THIS_MAKE) | REL/x86_64 + $(CXX) -o $@ -c $< $(RELEASE_x86_64_FLAGS) $(CXXFLAGS) + +# Define Link rule for 32 bit optimized and stripped NEXE +REL/$(PROJECT)_x86_32.nexe : $(RELEASE_x86_32_OBJS) + $(CXX) -o $@.unstripped $^ $(RELEASE_x86_32_FLAGS) $(LDFLAGS) + $(STRIP) $< -o $@ + +# Define Link rule for 64 bit optimized and stripped NEXE +REL/$(PROJECT)_x86_64.nexe : $(RELEASE_x86_64_OBJS) + $(CXX) -o $@.unstripped $^ $(RELEASE_x86_64_FLAGS) $(LDFLAGS) + $(STRIP) $@.unstripped -o $@ + +# Define a RELEASE alias to build the debug version +.PHONY : RELEASE RUN_RELEASE +RELEASE : REL/$(PROJECT)_x86_32.nexe REL/$(PROJECT)_x86_64.nexe $(REL_COPIES) + +# Define a RUN_RELEASE alias to build and server the RELEASE version +RUN_RELEASE: RELEASE + cd REL && python ../../httpd.py diff --git a/native_client_sdk/src/examples/gamepad/gamepad.cc b/native_client_sdk/src/examples/gamepad/gamepad.cc new file mode 100644 index 0000000..c288b8b --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/gamepad.cc @@ -0,0 +1,149 @@ +// Copyright (c) 2012 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gamepad.h" + +#include <stdio.h> +#include <stdlib.h> +#include <cassert> +#include <cmath> +#include <cstring> +#include <string> +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/var.h" + +namespace { + +// This is called by the browser when the 2D context has been flushed to the +// browser window. +void FlushCallback(void* data, int32_t result) { + static_cast<gamepad::Gamepad*>(data)->set_flush_pending(false); + static_cast<gamepad::Gamepad*>(data)->Paint(); +} + +} // namespace + +namespace gamepad { + +Gamepad::Gamepad(PP_Instance instance) + : pp::Instance(instance), + graphics_2d_context_(NULL), + pixel_buffer_(NULL), + flush_pending_(false), + quit_(false) { + pp::Module* module = pp::Module::Get(); + assert(module); + gamepad_ = static_cast<const PPB_Gamepad_Dev*>( + module->GetBrowserInterface(PPB_GAMEPAD_DEV_INTERFACE)); + assert(gamepad_); +} + +Gamepad::~Gamepad() { + quit_ = true; + DestroyContext(); + delete pixel_buffer_; +} + +void Gamepad::DidChangeView(const pp::Rect& position, + const pp::Rect& clip) { + if (position.size().width() == width() && + position.size().height() == height()) + return; // Size didn't change, no need to update anything. + + // Create a new device context with the new size. + DestroyContext(); + CreateContext(position.size()); + // Delete the old pixel buffer and create a new one. + delete pixel_buffer_; + pixel_buffer_ = NULL; + if (graphics_2d_context_ != NULL) { + pixel_buffer_ = new pp::ImageData(this, + PP_IMAGEDATAFORMAT_BGRA_PREMUL, + graphics_2d_context_->size(), + false); + } + Paint(); +} + +void FillRect(pp::ImageData* image, int left, int top, int width, int height, + uint32_t color) { + for (int y = std::max(0, top); + y < std::min(image->size().height() - 1, top + height); + y++) { + for (int x = std::max(0, left); + x < std::min(image->size().width() - 1, left + width); + x++) + *image->GetAddr32(pp::Point(x, y)) = color; + } +} + +void Gamepad::Paint() { + // Clear the background. + FillRect(pixel_buffer_, 0, 0, width(), height(), 0xfff0f0f0); + + // Get current gamepad data. + PP_GamepadsData_Dev gamepad_data; + gamepad_->SampleGamepads(pp_instance(), &gamepad_data); + + // Draw the current state for each connected gamepad. + for (size_t p = 0; p < gamepad_data.length; ++p) { + int width2 = width() / gamepad_data.length / 2; + int height2 = height() / 2; + int offset = width2 * 2 * p; + PP_GamepadData_Dev& pad = gamepad_data.items[p]; + + if (!pad.connected) + continue; + + // Draw axes. + for (size_t i = 0; i < pad.axes_length; i += 2) { + int x = static_cast<int>(pad.axes[i + 0] * width2 + width2) + offset; + int y = static_cast<int>(pad.axes[i + 1] * height2 + height2); + uint32_t box_bgra = 0x80000000; // Alpha 50%. + FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, box_bgra); + } + + // Draw buttons. + for (size_t i = 0; i < pad.buttons_length; ++i) { + float button_val = pad.buttons[i]; + uint32_t colour = static_cast<uint32_t>((button_val * 192) + 63) << 24; + int x = i * 8 + 10 + offset; + int y = 10; + FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, colour); + } + } + + // Output to the screen. + FlushPixelBuffer(); +} + +void Gamepad::CreateContext(const pp::Size& size) { + if (IsContextValid()) + return; + graphics_2d_context_ = new pp::Graphics2D(this, size, false); + if (!BindGraphics(*graphics_2d_context_)) { + printf("Couldn't bind the device context\n"); + } +} + +void Gamepad::DestroyContext() { + if (!IsContextValid()) + return; + delete graphics_2d_context_; + graphics_2d_context_ = NULL; +} + +void Gamepad::FlushPixelBuffer() { + if (!IsContextValid()) + return; + // Note that the pixel lock is held while the buffer is copied into the + // device context and then flushed. + graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point()); + if (flush_pending()) + return; + set_flush_pending(true); + graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); +} + +} // namespace gamepad diff --git a/native_client_sdk/src/examples/gamepad/gamepad.h b/native_client_sdk/src/examples/gamepad/gamepad.h new file mode 100644 index 0000000..e5a5e3a --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/gamepad.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXAMPLES_GAMEPAD_GAMEPAD_H_ +#define EXAMPLES_GAMEPAD_GAMEPAD_H_ + +#include <map> +#include <vector> +#include "ppapi/c/dev/ppb_gamepad_dev.h" +#include "ppapi/cpp/graphics_2d.h" +#include "ppapi/cpp/image_data.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/size.h" + +namespace gamepad { + +// The Instance class. One of these exists for each instance of your NaCl +// module on the web page. The browser will ask the Module object to create +// a new Instance for each occurrence of the <embed> tag that has these +// attributes: +// type="application/x-nacl" +// nacl="pi_generator.nmf" +class Gamepad : public pp::Instance { + public: + explicit Gamepad(PP_Instance instance); + virtual ~Gamepad(); + + // Update the graphics context to the new size, and regenerate |pixel_buffer_| + // to fit the new size as well. + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); + + // Flushes its contents of |pixel_buffer_| to the 2D graphics context. + void Paint(); + + bool quit() const { + return quit_; + } + + int width() const { + return pixel_buffer_ ? pixel_buffer_->size().width() : 0; + } + int height() const { + return pixel_buffer_ ? pixel_buffer_->size().height() : 0; + } + + // Indicate whether a flush is pending. This can only be called from the + // main thread; it is not thread safe. + bool flush_pending() const { + return flush_pending_; + } + void set_flush_pending(bool flag) { + flush_pending_ = flag; + } + + private: + // Create and initialize the 2D context used for drawing. + void CreateContext(const pp::Size& size); + // Destroy the 2D drawing context. + void DestroyContext(); + // Push the pixels to the browser, then attempt to flush the 2D context. If + // there is a pending flush on the 2D context, then update the pixels only + // and do not flush. + void FlushPixelBuffer(); + + bool IsContextValid() const { + return graphics_2d_context_ != NULL; + } + + pp::Graphics2D* graphics_2d_context_; + pp::ImageData* pixel_buffer_; + const PPB_Gamepad_Dev* gamepad_; + bool flush_pending_; + bool quit_; +}; + +} // namespace gamepad + +#endif // EXAMPLES_GAMEPAD_GAMEPAD_H_ diff --git a/native_client_sdk/src/examples/gamepad/gamepad.html b/native_client_sdk/src/examples/gamepad/gamepad.html new file mode 100644 index 0000000..65c0fff --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/gamepad.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <!-- + Copyright (c) 2012 The Native Client Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> + <head> + <title>Gamepad example</title> + </head> + <body id="bodyId"> + + <p> Attached gamepad values should appear, left to right, once they've been + interacted with. Buttons, esp triggers are analog (alpha). </p> + + <embed + name="nacl_module" + id="gamepad" + width=800 height=200 + src="gamepad.nmf" + type="application/x-nacl" /> + </body> +</html> diff --git a/native_client_sdk/src/examples/gamepad/gamepad.nmf b/native_client_sdk/src/examples/gamepad/gamepad.nmf new file mode 100644 index 0000000..53e3b97 --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/gamepad.nmf @@ -0,0 +1,6 @@ +{ + "program": { + "x86-64": {"url": "gamepad_x86_64.nexe"}, + "x86-32": {"url": "gamepad_x86_32.nexe"} + } +} diff --git a/native_client_sdk/src/examples/gamepad/gamepad_module.cc b/native_client_sdk/src/examples/gamepad/gamepad_module.cc new file mode 100644 index 0000000..cac2570 --- /dev/null +++ b/native_client_sdk/src/examples/gamepad/gamepad_module.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <ppapi/cpp/module.h> + +#include "gamepad.h" + +namespace gamepad { +// The Module class. The browser calls the CreateInstance() method to create +// an instance of your NaCl module on the web page. The browser creates a new +// instance for each <embed> tag with type="application/x-nacl". +class GamepadModule : public pp::Module { + public: + GamepadModule() : pp::Module() {} + virtual ~GamepadModule() {} + + // Create and return a GamepadInstance object. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new Gamepad(instance); + } +}; +} // namespace gamepad + +// Factory function called by the browser when the module is first loaded. +// The browser keeps a singleton of this module. It calls the +// CreateInstance() method on the object you return to make instances. There +// is one instance per <embed> tag on the page. This is the main binding +// point for your NaCl module with the browser. +namespace pp { +Module* CreateModule() { + return new gamepad::GamepadModule(); +} +} // namespace pp |