Merge pull request #1016 from atom/screen-improve

Add API to enumerate external displays in screen module
This commit is contained in:
Cheng Zhao 2015-01-18 10:23:18 -08:00
commit e8f1f6819c
10 changed files with 432 additions and 117 deletions

View file

@ -25,6 +25,7 @@
'atom/browser/api/lib/menu-item.coffee',
'atom/browser/api/lib/power-monitor.coffee',
'atom/browser/api/lib/protocol.coffee',
'atom/browser/api/lib/screen.coffee',
'atom/browser/api/lib/tray.coffee',
'atom/browser/api/lib/web-contents.coffee',
'atom/browser/lib/chrome-extension.coffee',
@ -38,7 +39,6 @@
'atom/common/api/lib/crash-reporter.coffee',
'atom/common/api/lib/id-weak-map.coffee',
'atom/common/api/lib/original-fs.coffee',
'atom/common/api/lib/screen.coffee',
'atom/common/api/lib/shell.coffee',
'atom/common/lib/init.coffee',
'atom/common/lib/asar.coffee',
@ -52,6 +52,7 @@
'atom/renderer/lib/web-view/web-view-constants.coffee',
'atom/renderer/api/lib/ipc.coffee',
'atom/renderer/api/lib/remote.coffee',
'atom/renderer/api/lib/screen.coffee',
'atom/renderer/api/lib/web-frame.coffee',
],
'lib_sources': [
@ -78,6 +79,8 @@
'atom/browser/api/atom_api_power_monitor.h',
'atom/browser/api/atom_api_protocol.cc',
'atom/browser/api/atom_api_protocol.h',
'atom/browser/api/atom_api_screen.cc',
'atom/browser/api/atom_api_screen.h',
'atom/browser/api/atom_api_tray.cc',
'atom/browser/api/atom_api_tray.h',
'atom/browser/api/atom_api_web_contents.cc',
@ -199,8 +202,6 @@
'atom/common/api/atom_api_crash_reporter.cc',
'atom/common/api/atom_api_id_weak_map.cc',
'atom/common/api/atom_api_id_weak_map.h',
'atom/common/api/atom_api_screen.cc',
'atom/common/api/atom_api_screen.h',
'atom/common/api/atom_api_shell.cc',
'atom/common/api/atom_api_v8_util.cc',
'atom/common/api/atom_bindings.cc',
@ -234,6 +235,7 @@
'atom/common/native_mate_converters/accelerator_converter.cc',
'atom/common/native_mate_converters/accelerator_converter.h',
'atom/common/native_mate_converters/file_path_converter.h',
'atom/common/native_mate_converters/gfx_converter.cc',
'atom/common/native_mate_converters/gfx_converter.h',
'atom/common/native_mate_converters/gurl_converter.h',
'atom/common/native_mate_converters/image_converter.cc',

View file

@ -0,0 +1,135 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_api_screen.h"
#include <algorithm>
#include <string>
#include "atom/browser/browser.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "base/bind.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "ui/gfx/screen.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
namespace {
// Find an item in container according to its ID.
template<class T>
typename T::iterator FindById(T* container, int id) {
auto predicate = [id] (const typename T::value_type& item) -> bool {
return item.id() == id;
};
return std::find_if(container->begin(), container->end(), predicate);
}
// Convert the changed_metrics bitmask to string array.
std::vector<std::string> MetricsToArray(uint32_t metrics) {
std::vector<std::string> array;
if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS)
array.push_back("bounds");
if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA)
array.push_back("workArea");
if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR)
array.push_back("scaleFactor");
if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_ROTATION)
array.push_back("rotaion");
return array;
}
} // namespace
Screen::Screen(gfx::Screen* screen) : screen_(screen) {
screen_->AddObserver(this);
}
Screen::~Screen() {
screen_->RemoveObserver(this);
}
gfx::Point Screen::GetCursorScreenPoint() {
return screen_->GetCursorScreenPoint();
}
gfx::Display Screen::GetPrimaryDisplay() {
return screen_->GetPrimaryDisplay();
}
std::vector<gfx::Display> Screen::GetAllDisplays() {
// The Screen::GetAllDisplays doesn't update when there is display added or
// removed, so we have to manually maintain the displays_ to make it up to
// date.
if (displays_.size() == 0)
displays_ = screen_->GetAllDisplays();
return displays_;
}
gfx::Display Screen::GetDisplayNearestPoint(const gfx::Point& point) {
return screen_->GetDisplayNearestPoint(point);
}
gfx::Display Screen::GetDisplayMatching(const gfx::Rect& match_rect) {
return screen_->GetDisplayMatching(match_rect);
}
void Screen::OnDisplayAdded(const gfx::Display& new_display) {
displays_.push_back(new_display);
Emit("display-added", new_display);
}
void Screen::OnDisplayRemoved(const gfx::Display& old_display) {
displays_.erase(FindById(&displays_, old_display.id()));
Emit("display-removed", old_display);
}
void Screen::OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t changed_metrics) {
*FindById(&displays_, display.id()) = display;
Emit("display-metrics-changed", display, MetricsToArray(changed_metrics));
}
mate::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("getCursorScreenPoint", &Screen::GetCursorScreenPoint)
.SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay)
.SetMethod("getAllDisplays", &Screen::GetAllDisplays)
.SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint)
.SetMethod("getDisplayMatching", &Screen::GetDisplayMatching);
}
// static
mate::Handle<Screen> Screen::Create(v8::Isolate* isolate) {
gfx::Screen* screen = gfx::Screen::GetNativeScreen();
if (!screen) {
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate, "Failed to get screen information")));
return mate::Handle<Screen>();
}
return CreateHandle(isolate, new Screen(screen));
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.Set("screen", atom::api::Screen::Create(context->GetIsolate()));
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_common_screen, Initialize)

View file

@ -0,0 +1,60 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_SCREEN_H_
#define ATOM_BROWSER_API_ATOM_API_SCREEN_H_
#include <vector>
#include "atom/browser/api/event_emitter.h"
#include "native_mate/handle.h"
#include "ui/gfx/display_observer.h"
namespace gfx {
class Point;
class Rect;
class Screen;
}
namespace atom {
namespace api {
class Screen : public mate::EventEmitter,
public gfx::DisplayObserver {
public:
static mate::Handle<Screen> Create(v8::Isolate* isolate);
protected:
explicit Screen(gfx::Screen* screen);
virtual ~Screen();
gfx::Point GetCursorScreenPoint();
gfx::Display GetPrimaryDisplay();
std::vector<gfx::Display> GetAllDisplays();
gfx::Display GetDisplayNearestPoint(const gfx::Point& point);
gfx::Display GetDisplayMatching(const gfx::Rect& match_rect);
// gfx::DisplayObserver:
void OnDisplayAdded(const gfx::Display& new_display) override;
void OnDisplayRemoved(const gfx::Display& old_display) override;
void OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t changed_metrics) override;
// mate::Wrappable:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
private:
gfx::Screen* screen_;
std::vector<gfx::Display> displays_;
DISALLOW_COPY_AND_ASSIGN(Screen);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_SCREEN_H_

View file

@ -0,0 +1,6 @@
EventEmitter = require('events').EventEmitter
screen = process.atomBinding('screen').screen
screen.__proto__ = EventEmitter.prototype
module.exports = screen

View file

@ -1,24 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/node_includes.h"
namespace {
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
v8::Handle<v8::Context> context, void* priv) {
gfx::Screen* screen = gfx::Screen::GetNativeScreen();
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("getCursorScreenPoint",
base::Bind(&gfx::Screen::GetCursorScreenPoint,
base::Unretained(screen)));
dict.SetMethod("getPrimaryDisplay",
base::Bind(&gfx::Screen::GetPrimaryDisplay,
base::Unretained(screen)));
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_common_screen, Initialize)

View file

@ -1,17 +0,0 @@
binding = process.atomBinding 'screen'
checkAppIsReady = ->
unless process.type is 'renderer' or require('app').isReady()
throw new Error('Can not use screen module before the "ready" event of app module gets emitted')
module.exports =
if process.platform in ['linux', 'win32'] and process.type is 'renderer'
# On Linux we could not access screen in renderer process.
require('remote').require 'screen'
else
getCursorScreenPoint: ->
checkAppIsReady()
binding.getCursorScreenPoint()
getPrimaryDisplay: ->
checkAppIsReady()
binding.getPrimaryDisplay()

View file

@ -0,0 +1,110 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "native_mate/dictionary.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size.h"
namespace mate {
v8::Handle<v8::Value> Converter<gfx::Point>::ToV8(v8::Isolate* isolate,
const gfx::Point& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
return dict.GetHandle();
}
bool Converter<gfx::Point>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Point* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
*out = gfx::Point(x, y);
return true;
}
v8::Handle<v8::Value> Converter<gfx::Size>::ToV8(v8::Isolate* isolate,
const gfx::Size& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
bool Converter<gfx::Size>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Size* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int width, height;
if (!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Size(width, height);
return true;
}
v8::Handle<v8::Value> Converter<gfx::Rect>::ToV8(v8::Isolate* isolate,
const gfx::Rect& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
bool Converter<gfx::Rect>::FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Rect* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y, width, height;
if (!dict.Get("x", &x) || !dict.Get("y", &y) ||
!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Rect(x, y, width, height);
return true;
}
template<>
struct Converter<gfx::Display::TouchSupport> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Display::TouchSupport& val) {
switch (val) {
case gfx::Display::TOUCH_SUPPORT_UNKNOWN:
return StringToV8(isolate, "unknown");
case gfx::Display::TOUCH_SUPPORT_AVAILABLE:
return StringToV8(isolate, "available");
case gfx::Display::TOUCH_SUPPORT_UNAVAILABLE:
return StringToV8(isolate, "unavailable");
}
}
};
v8::Handle<v8::Value> Converter<gfx::Display>::ToV8(v8::Isolate* isolate,
const gfx::Display& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("id", val.id());
dict.Set("bounds", val.bounds());
dict.Set("workArea", val.work_area());
dict.Set("size", val.size());
dict.Set("workAreaSize", val.work_area_size());
dict.Set("scaleFactor", val.device_scale_factor());
dict.Set("rotation", val.RotationAsDegree());
dict.Set("touchSupport", val.touch_support());
return dict.GetHandle();
}
} // namespace mate

View file

@ -5,95 +5,51 @@
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_GFX_CONVERTER_H_
#include "native_mate/dictionary.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size.h"
#include "native_mate/converter.h"
namespace gfx {
class Point;
class Size;
class Rect;
class Display;
}
namespace mate {
template<>
struct Converter<gfx::Point> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Point& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Point* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y;
if (!dict.Get("x", &x) || !dict.Get("y", &y))
return false;
*out = gfx::Point(x, y);
return true;
}
const gfx::Point& val);
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Point* out);
};
template<>
struct Converter<gfx::Size> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Size& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Size* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int width, height;
if (!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Size(width, height);
return true;
}
const gfx::Size& val);
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Size* out);
};
template<>
struct Converter<gfx::Rect> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Rect& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("x", val.x());
dict.Set("y", val.y());
dict.Set("width", val.width());
dict.Set("height", val.height());
return dict.GetHandle();
}
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
gfx::Rect* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
int x, y, width, height;
if (!dict.Get("x", &x) || !dict.Get("y", &y) ||
!dict.Get("width", &width) || !dict.Get("height", &height))
return false;
*out = gfx::Rect(x, y, width, height);
return true;
}
const gfx::Rect& val);
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Rect* out);
};
template<>
struct Converter<gfx::Display> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
const gfx::Display& display) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("bounds", display.bounds());
dict.Set("workArea", display.work_area());
dict.Set("size", display.size());
dict.Set("workAreaSize", display.work_area_size());
dict.Set("scaleFactor", display.device_scale_factor());
return dict.GetHandle();
}
const gfx::Display& val);
static bool FromV8(v8::Isolate* isolate,
v8::Handle<v8::Value> val,
gfx::Display* out);
};
} // namespace mate

View file

@ -0,0 +1 @@
module.exports = require('remote').require('screen')

View file

@ -1,13 +1,77 @@
# screen
Gets various info about screen size, displays, cursor position, etc.
```js
var Screen = require('screen');
Gets various info about screen size, displays, cursor position, etc. You should
not use this module until the `ready` event of `app` module gets emitted.
`screen` is an [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter).
An example of creating a window that fills the whole screen:
```javascript
var app = require('app');
var BrowserWindow = require('browser-window');
var size = Screen.getPrimaryDisplay().workAreaSize;
mainWindow = new BrowserWindow({ width: size.width, height: size.height });
var mainWindow;
app.on('ready', function() {
var screen = require('screen');
var size = screen.getPrimaryDisplay().workAreaSize;
mainWindow = new BrowserWindow({ width: size.width, height: size.height });
});
```
Another example of creating a window in the external display:
```javascript
var app = require('app');
var BrowserWindow = require('browser-window');
var mainWindow;
app.on('ready', function() {
var screen = require('screen');
var displays = screen.getAllDisplays();
var externalDisplay = null;
for (var i in displays) {
if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) {
externalDisplay = displays[i];
break;
}
}
if (externalDisplay) {
mainWindow = new BrowserWindow({
x: externalDisplay.bounds.x + 50,
y: externalDisplay.bounds.y + 50,
});
}
});
```
## Event: display-added
* `event` Event
* `newDisplay` Object
Emitted when `newDisplay` has been added.
## Event: display-removed
* `event` Event
* `oldDisplay` Object
Emitted when `oldDisplay` has been removed.
## Event: display-metrics-changed
* `event` Event
* `display` Object
* `changedMetricts` Array
Emitted when a `display` has one or more metrics changed, `changedMetricts` is
an array of strings that describe the changes. Possible changes are `bounds`,
`workArea`, `scaleFactor` and `rotation`.
## screen.getCursorScreenPoint()
Returns the current absolute position of the mouse pointer.
@ -15,3 +79,25 @@ Returns the current absolute position of the mouse pointer.
## screen.getPrimaryDisplay()
Returns the primary display.
## screen.getAllDisplays()
Returns an array of displays that are currently available.
## screen.getDisplayNearestPoint(point)
* `point` Object
* `x` Integer
* `y` Integer
Returns the display nearest the specified point.
## screen.getDisplayMatching(rect)
* `rect` Object
* `x` Integer
* `y` Integer
* `width` Integer
* `height` Integer
Returns the display that most closely intersects the provided bounds.