NX
Overview
NX provides a tiny windowing system in the spirit of X, but greatly scaled down and appropriate for most resource-limited embedded environments. The current NX implementation supports the general following, high-level features:
Virtual Vertical Graphics Space Windows that reside in a virtual, vertical space so that it makes sense to talk about one window being on top of another and obscuring the window below it.
Client/Server Model A standard client server/model was adopted. NX may be considered a server and other logic that presents the windows are NX clients.
Multi-User Support NX includes front-end logic to an NX server daemon that can serve multiple NX client threads. The NX sever thread/daemon serializes graphics operations from multiple clients.
Minimal Graphics Toolset The actual implementation of the graphics operations is performed by common, back-end logic. This back-end supports only a primitive set of graphic and rendering operations.
Device Interface NX supports any graphics device either of two device interfaces:
Any device with random access video memory using the NuttX framebuffer driver interface (see include/nuttx/video/fb.h).
Any LCD-like device than can accept raster line runs through a parallel or serial interface (see include/nuttx/lcd/lcd.h). By default, NX is configured to use the frame buffer driver unless CONFIG_NX_LCDDRIVER is defined =y in your NuttX configuration file.
Transparent to NX Client The window client on “sees” the sub-window that is operates in and does not need to be concerned with the virtual, vertical space (other that to respond to redraw requests from NX when needed).
Framed Windows and Toolbars NX also adds the capability to support windows with frames and toolbars on top of the basic windowing support. These are windows such as those shown in the screenshot above. These framed windows sub-divide one one window into three relatively independent subwindows: A frame, the contained window and an (optional) toolbar window.
Mouse Support NX provides support for a mouse or other X/Y pointing devices. APIs are provided to allow external devices to give X/Y position information and mouse button presses to NX. NX will then provide the mouse input to the relevant window clients via callbacks. Client windows only receive the mouse input callback if the mouse is positioned over a visible portion of the client window; X/Y position is provided to the client in the relative coordinate system of the client window.
Keyboard input NX also supports keyboard/keypad devices. APIs are provided to allow external devices to give keypad information to NX. NX will then provide the mouse input to the top window on the display (the window that has the focus) via a callback function.
Pre-Processor Definitions
The default server message queue name used by the nx_run
macro:
#define NX_DEFAULT_SERVER_MQNAME "/dev/nxs"
Mouse button bits:
#define NX_MOUSE_NOBUTTONS 0x00
#define NX_MOUSE_LEFTBUTTON 0x01
#define NX_MOUSE_CENTERBUTTON 0x02
#define NX_MOUSE_RIGHTBUTTON 0x04
NX Types
The interface to the NX server is managed using a opaque handle:
-
typedef FAR void *NXHANDLE
The interface to a specific window is managed using an opaque handle:
-
typedef FAR void *NXWINDOW
These define callbacks that must be provided to nx_openwindow()
.
These callbacks will be invoked as part of the processing performed by
nx_eventhandler()
.
-
struct nx_callback_s
struct nx_callback_s { void (*redraw)(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, bool more, FAR void *arg); void (*position)(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg); #ifdef CONFIG_NX_XYINPUT void (*mousein)(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, uint8_t buttons, FAR void *arg); #endif #ifdef CONFIG_NX_KBD void (*kbdin)(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, FAR void *arg); #endif };
Starting the NX Server
The NX Server is a kernel daemon that receives and serializes graphic commands. Before you can use the NX graphics system, you must first start this daemon. There are two ways that this can be done:
The NX server may be started in your board startup logic by simply calling the function
nxmu_start()
. The board startup logic usually resides the theboards/arch/chip/board/src
directory. The board startup logic can run automatically during the early system ifCONFIG_BOARD_LATE_INITIALIZE
is defined in the configuration. Or, the board startup logic can execute under control of the application by callingboardctl()
as:boardctl(BOARDIOC_INIT, arg)
The board initialization logic will run in either case and the simple call to
nxmu_start()
will start the NX server.The NX server may also be started later by the application via
boardctl()
as:boardctl(BOARDIOC_NX_START, arg)
-
int nxmu_start(int display, int plane);
Provides a wrapper function to simplify and standardize the starting of the NX server.
- Parameters:
display – The display number to be served by this new NXMU instance.
plane – The plane number to use to get information about the display geometry and color format.
- Returns:
Zero (
OK
) is returned on success. This indicates that the NX server has been successfully started, is running, and waiting to accept connections from NX clients. A negatederrno
value is returned on failure. Theerrno
value indicates the nature of the failure.
NX Server Callbacks
-
void redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, bool more, FAR void *arg);
NX requests that the client re-draw the portion of the window within with rectangle.
- Parameters:
hwnd – The handle created by
nx_openwindow()
ornx_requestbkgd()
rect – The rectangle that needs to be re-drawn (in window relative coordinates)
more – true: More re-draw requests will follow
arg – User provided argument (see
nx_openwindow()
)
-
void position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg);
The size or position of the window has changed (or the window was just created with zero size.
- Parameters:
hwnd – The handle created by
nx_openwindow()
ornx_requestbkgd()
size – The size of the window
pos – The position of the upper left hand corner of the window on the overall display
bounds – The bounding rectangle that the describes the entire display
arg – User provided argument (see
nx_openwindow()
)
-
void mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, uint8_t buttons, FAR void *arg);
New mouse data is available for the window
- Parameters:
hwnd – The handle created by
nx_openwindow()
ornx_requestbkgd()
pos – The (x,y) position of the mouse
buttons – See
NX_MOUSE_*
definitionsarg – User provided argument (see
nx_openwindow()
)
-
void (*kbdin)(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, FAR void *arg);
New keyboard/keypad data is available for the window.
- Param hwnd:
The handle created by
nx_openwindow()
ornx_requestbkgd()
- Param nch:
The number of characters that are available in ch[]
- Param ch:
The array of characters
- Param arg:
User provided argument (see
nx_openwindow()
)
-
void (*event)(NXWINDOW hwnd, enum nx_event_e event, FAR void *arg1, FAR void *arg2);
This callback is used to communicate server events to the window listener.
NXEVENT_BLOCKED
: Window messages are blocked.This callback is the response from
nx_block()
,nxtk_block()
. Those blocking interfaces are used to assure that no further messages are directed to the window. Receipt of the blocked callback signifies that (1) there are no further pending callbacks and (2) that the window is now defunct and will receive no further callbacks. This callback supports coordinated destruction of a window. In the multi-user mode, the client window logic must stay intact until all of the queued callbacks are processed. Then the window may be safely closed. Closing the window prior with pending callbacks can lead to bad behavior when the callback is executed.
NXEVENT_SYNCHED
: Synchronization handshakeThis completes the handshake started by
nx_synch()
, ornxtk_synch()
. Those interfaces send a synchronization messages to the NX server which responds with this event. The sleeping client is awakened and continues graphics processing, completing the handshake. Due to the highly asynchronous nature of client-server communications, synchronization is sometimes necessary to assure that the client and server are working together properly.
- Param hwnd:
TWindow handle of window receiving the event
- Param event:
The server event
- Param arg1:
User provided argument (see
nx_openwindow()
,nx_requestbkgd()
, ornxtk_opentoolbar()
)- Param arg2:
TUser provided argument (see
nx_block()
,nxtk_block()
,nx_synch()
, ornxtk_synch()
)
-
nx_run(fb)
#define nx_run(fb) nx_runinstance(NX_DEFAULT_SERVER_MQNAME, dev)
-
int nx_runinstance(FAR const char *mqname, FAR struct fb_vtable_s *fb)
This is the server entry point. It does not return; the calling thread is dedicated to supporting NX server.
NOTE that multiple instances of the NX server may run at the same time, with different callback and message queue names.
nx_run()
is simply a macro that can be used when only one server instance is required. In that case, a default server name is used.- Parameters:
mqname – The name for the server incoming message queue
dev – Framebuffer or LCD driver “object” to be used
- Returns:
This function usually does not return. If it does return, it will return
ERROR
anderrno
will be set appropriately.
-
nx_connect(cb)
#define nx_connect(cb) nx_connectinstance(NX_DEFAULT_SERVER_MQNAME)
-
NXHANDLE nx_connectinstance(FAR const char *svrmqname);
Open a connection from a client to the NX server. One one client connection is normally needed per thread as each connection can host multiple windows.
NOTES:
This function returns before the connection is fully instantiated. it is necessary to wait for the connection event before using the returned handle.
Multiple instances of the NX server may run at the same time, each with different message queue names.
nx_connect()
is simply a macro that can be used when only one server instance is required. In that case, a default server name is used.
- Parameters:
svrmqname – The name for the server incoming message queue
- Returns:
Success: A non-NULL handle used with subsequent NX accesses Failure: NULL is returned and errno is set appropriately.
-
void nx_disconnect(NXHANDLE handle)
Disconnect a client from the NX server and/or free resources reserved by
nx_connect()
/c:func:nx_connectinstance.- Parameters:
handle – The handle returned by
nx_connectinstance()
.
-
int nx_eventhandler(NXHANDLE handle);
The client code must call this function periodically to process incoming messages from the server. If
CONFIG_NX_BLOCKING
is defined, then this function not return until a server message is received.When
CONFIG_NX_BLOCKING
is not defined, the client must exercise caution in the looping to assure that it does not eat up all of the CPU bandwidth calling nx_eventhandler repeatedly.`nx_eventnotify()
<#nxeventnotify>`__ may be called to get a signal event whenever a new incoming server event is available.- Parameters:
handle – The handle returned by
`nx_connect()
<#nxconnectinstance>`__.
- Returns:
OK
: No errors occurred. IfCONFIG_NX_BLOCKING
is defined, then one or more server messages were processed.ERROR
: An error occurred anderrno
has been set appropriately. Of particular interest, it will returnerrno == EHOSTDOWN
when the server is disconnected. After that event, the handle can no longer be used.
-
int nx_eventnotify(NXHANDLE handle, int signo);
Rather than calling
nx_eventhandler()
periodically, the client may register to receive a signal when a server event is available. The client can then call :c:func:nx_eventhandler` only when incoming events are available.The underlying implementation used
mq_notifiy()
and, as a result, the client must observe the rules for usingmq_notifiy()
:Only one event is signalled. Upon receipt of the signal, if the client wishes further notifications, it must call
nx_eventnotify()
again.The signal will only be issued when the message queue transitions from empty to not empty.
- Parameters:
handle – The handle returned by
`nx_connect()
<#nxconnectinstance>`__.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_block(NXWINDOW hwnd, FAR void *arg);
The response to this function call is two things: (1) any queued callback messages to the window are ‘blocked’ and then (2) also subsequent window messaging is blocked.
The
event
callback with theNXEVENT_BLOCKED
event is the response fromnx_block()
. This blocking interface is used to assure that no further messages are are directed to the window. Receipt of theNXEVENT_BLOCKED
event signifies that (1) there are no further pending callbacks and (2) that the window is now defunct and will receive no further callbacks.This callback supports coordinated destruction of a window. The client window logic must stay intact until all of the queued callbacks are processed. Then the window may be safely closed. Closing the window prior with pending callbacks can lead to bad behavior when the callback is executed.
- Parameters:
wnd – The window to be blocked
arg – An argument that will accompany the block messages (This is
arg2
in the event callback).
- Returns:
OK on success; ERROR on failure with errno set appropriately.
-
int nx_synch(NXWINDOW hwnd, FAR void *arg);
This interface can be used to synchronize the window client with the NX server. It really just implements an echo: A synch message is sent from the window client to the server which then responds immediately by sending the
NXEVENT_SYNCHED
back to the windows client.Due to the highly asynchronous nature of client-server communications,
nx_synch()
is sometimes necessary to assure that the client and server are fully synchronized in time.Usage by the window client might be something like this:
extern bool g_synched; extern sem_t g_synch_sem; g_synched = false; ret = nx_synch(hwnd, handle); if (ret < 0) { -- Handle the error -- } while (!g_synched) { ret = sem_wait(&g_sync_sem); if (ret < 0) { -- Handle the error -- } }
When the window listener thread receives the
NXEVENT_SYNCHED
event, it would setg_synched
totrue
and postg_synch_sem
, waking up the above loop.- Parameters:
wnd – The window to be synched
arg – An argument that will accompany the synch messages (This is
arg2
in the event callback).
- Returns:
OK on success; ERROR on failure with errno set appropriately
-
NXWINDOW nx_openwindow(NXHANDLE handle, uint8_t flags, FAR const struct nx_callback_s *cb, FAR void *arg);
Create a new window.
- Parameters:
handle – The handle returned by
`nx_connect()
<#nxconnectinstance>`__.flags – Optional flags. These include: -
NXBE_WINDOW_RAMBACKED
: Creates a RAM backed window. This option is only valid ifCONFIG_NX_RAMBACKED
is enabled. -NXBE_WINDOW_HIDDEN
: The window is create in the HIDDEN state and can be made visible later withnx_setvisibility()
.cb – Callbacks used to process window events
arg – User provided value that will be returned with NX callbacks.
- Returns:
Success: A non-NULL handle used with subsequent NX accesses Failure: NULL is returned and errno is set appropriately.
-
int nx_closewindow(NXWINDOW hwnd)
Destroy a window created by
nx_openwindow()
window.- Parameters:
hwnd – The handle returned by
`nx_openwindow()
<#nxopenwindow>`__ that identifies the window to be destroyed. This handle must not have been one returned by`nx_requestbkgd()
<#nxrequestbkgd>`__.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_requestbkgd(NXHANDLE handle, FAR const struct nx_callback_s *cb, FAR void *arg);
NX normally controls a separate window called the background window. It repaints the window as necessary using only a solid color fill. The background window always represents the entire screen and is always below other windows. It is useful for an application to control the background window in the following conditions:
If you want to implement a windowless solution. The single screen can be used to create a truly simple graphic environment.
When you want more on the background than a solid color. For example, if you want an image in the background, or animations in the background, or live video, etc.
This API only requests the handle of the background window. That handle will be returned asynchronously in a subsequent position and redraw callbacks.
Cautions:
The following should never be called using the background window. They are guaranteed to cause severe crashes:
nx_setposition()
,nx_setsize()
,nx_raise()
, ornx_lower()
,nx_modal()
,nx_setvisibility()
.Neither
nx_requestbkgd()
nornx_releasebkgd()
should be called more than once. Multiple instances of the background window are not supported.
- Parameters:
handle – The handle returned by
`nx_connect()
<#nxconnectinstance>`__.cb – Callbacks to use for processing background window events
arg – User provided argument (see
`nx_openwindow()
<#nxopenwindow>`__)
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_releasebkgd(NXWINDOW hwnd)
Release the background window previously acquired using
nx_requestbkgd()
and return control of the background to NX.- Parameters:
handle – The handle returned indirectly by
nx_requestbkgd()
. This handle must not have been one created bynx_openwindow()
.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_getposition(NXWINDOW hwnd)
Request the position and size information for the selected window. The values will be return asynchronously through the client callback function pointer.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_setposition(NXWINDOW hwnd, FAR struct nxgl_point_s *pos)
Set the position and size for the selected window.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.pos – The new position of the window
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_setsize(NXWINDOW hwnd, FAR struct nxgl_size_s *size)
Set the size of the selected window.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.size – The new size of the window (in pixels).
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_raise(NXWINDOW hwnd)
Bring the specified window to the top of the display.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_lower(NXWINDOW hwnd);
Lower the specified window to the bottom of the display.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_modal(NXWINDOW hwnd, bool modal)
May be used to either (1) raise a window to the top of the display and select modal behavior, or (2) disable modal behavior.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.modal – True: enter modal state; False: leave modal state
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_setvisibility(NXWINDOW hwnd, bool hide);
Select if the window is visible or hidden. A hidden window is still present and will update normally, but will not be visible on the display until it is unhidden.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
. This handle must not have been created bynx_requestbkgd()
.hide – True: Window will be hidden; false: Window will be visible
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
Return true if the window is hidden.
NOTE: There will be a delay between the time that the visibility of the window is changed via
nx_setvisibily()
before that new setting is reported bynx_ishidden()
.nx_synch()
may be used if temporal synchronization is required.- Parameters:
hwnd – The handle returned by
nx_openwindow()
that identifies the window to be queried.
- Returns:
True: the window is hidden, false: the window is visible
-
int nx_fill(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, nxgl_mxpixel_t color[CONFIG_NX_NPLANES]);
Fill the specified rectangle in the window with the specified color.
- Parameters:
hwnd – The handle returned by
`nx_openwindow()
<#nxopenwindow>`__ or`nx_requestbkgd()
<#nxrequestbkgd>`__rect – The location to be filled
color – The color to use in the fill
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
void nx_getrectangle(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, unsigned int plane, FAR uint8_t *dest, unsigned int deststride);
Get the raw contents of graphic memory within a rectangular region. NOTE: Since raw graphic memory is returned, the returned memory content may be the memory of windows above this one and may not necessarily belong to this window unless you assure that this is the top window.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
rect – The location to be copied
plane – Specifies the color plane to get from
dest – The location to copy the memory region
deststride – The width, in bytes, of the dest memory
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_filltrapezoid(NXWINDOW hwnd, FAR const struct nxgl_rect_s *clip, FAR const struct nxgl_trapezoid_s *trap, nxgl_mxpixel_t color[CONFIG_NX_NPLANES]);
Fill the specified trapezoidal region in the window with the specified color.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
clip – Clipping rectangle relative to window (may be null)
trap – The trapezoidal region to be filled
color – The color to use in the fill
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_drawline(NXWINDOW hwnd, FAR struct nxgl_vector_s *vector, nxgl_coord_t width, nxgl_mxpixel_t color[CONFIG_NX_NPLANES], uint8_t caps);
Fill the specified trapezoidal region in the window with the specified color. Fill the specified line in the window with the specified color. This is simply a wrapper that uses
nxgl_splitline()
to break the line into trapezoids and then callsnx_filltrapezoid()
to render the line.- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
vector – Describes the line to be drawn.
width – The width of the line
color – The color to use to fill the line
caps –
Draw a circular cap on the ends of the line to support better line joins. One of:
/* Line caps */ #define NX_LINECAP_NONE 0x00, /* No line caps */ #define NX_LINECAP_PT1 0x01 /* Line cap on pt1 on of the vector only */ #define NX_LINECAP_PT2 0x02 /* Line cap on pt2 on of the vector only */ #define NX_LINECAP_BOTH 0x03 /* Line cap on both ends of the vector only */
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_drawcircle(NXWINDOW hwnd, FAR const struct nxgl_point_s *center, nxgl_coord_t radius, nxgl_coord_t width, nxgl_mxpixel_t color[CONFIG_NX_NPLANES]);
Draw a circular outline using the specified line thickness and color.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
center – A pointer to the point that is the center of the circle.
radius – The radius of the circle in pixels.
width – The width of the line
color – The color to use to fill the line
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_fillcircle(NXWINDOW hwnd, FAR const struct nxgl_point_s *center, nxgl_coord_t radius, nxgl_mxpixel_t color[CONFIG_NX_NPLANES]);
Fill a circular region using the specified color.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
center – A pointer to the point that is the center of the circle.
radius – The width of the line
color – The color to use to fill the circle
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_setbgcolor(NXHANDLE handle, nxgl_mxpixel_t color[CONFIG_NX_NPLANES]);
Set the color of the background.
- param handle:
The handle created by
nx_openwindow()
ornx_requestbkgd()
- param color:
The color to use in the background
- return:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_move(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, FAR const struct nxgl_point_s *offset);
Move a rectangular region within the window.
- param hwnd:
The handle returned by
nx_openwindow()
ornx_requestbkgd()
that specifies the window within which the move is to be done- param rect:
Describes the (source) rectangular region to move
- param offset:
The offset to move the region
- return:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_bitmap(NXWINDOW hwnd, FAR const struct nxgl_rect_s *dest, FAR const void *src[CONFIG_NX_NPLANES], FAR const struct nxgl_point_s *origin, unsigned int stride);
Copy a rectangular region of a larger image into the rectangle in the specified window.
- Parameters:
hwnd – The handle returned by
nx_openwindow()
ornx_requestbkgd()
that specifies the window that will receive the bitmap image.dest – Describes the rectangular on the display that will receive the bit map.
src – The start of the source image. This is an array source images of size
CONFIG_NX_NPLANES
(probably 1).origin – The origin of the upper, left-most corner of the full bitmap. Both dest and origin are in window coordinates, however, the origin may lie outside of the display.
stride – The width of the full source image in bytes.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_kbdin(NXHANDLE handle, uint8_t nch, FAR const uint8_t *ch);
Used by a thread or interrupt handler that manages some kind of keypad hardware to report text information to the NX server. That text data will be routed by the NX server to the appropriate window client.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately
-
int nx_mousein(NXHANDLE handle, nxgl_coord_t x, nxgl_coord_t y, uint8_t buttons)
Used by a thread or interrupt handler that manages some kind of pointing hardware to report new positional data to the NX server. That positional data will be routed by the NX server to the appropriate window client.
- Returns:
OK
on success;ERROR
on failure witherrno
set appropriately