Archive

Author Archive

wxHaskell News

April 18, 2012 5 comments

A great deal has happened in wxHaskell land over the past few weeks, so I thought a summary was worthwhile

wxHaskell 0.90 Released

A significant update to wxHaskell was released on April 14th. This brings in all of the work done by Dave Tapley, Eric and many others to provide support for wxWidgets 2.9.

Supporting wxWidgets 2.9 is important for quite a number of reasons – not least because at some point it will become wxWidgets 3.0 and will be the new stable version. However in the short term the main benefit is support for 64bit OS platforms – notably MacOS X Snow Leopard and Lion.

The slightly odd version numbering convention was chosen to allow wxHaskell 0.13 to evolve without being excessively constrained over version numbering. In any case, it would be nice to get to version 1.0 at some time soon – perhaps when wxWidgets 3.0 is released.

Most of the future wxHaskell development effort will go on the new branch.

wxHaskell 0.13 Branch Created

On many systems, particularly almost all Linux distributions, wxWidgets 2.8.x remains the standard ‘package’ for wxWidgets, so we continue to support this for those who would prefer to use the packages provided by their distro. It also allows Windows users without C++ development environments to use the wxPack binary installers for wxWidgets.

Experimental GitHub Repository

I have created an experimental GitHub repository. It is right up to date at the time of writing and contains two active branches: master is the wxWidgets 2.9 repo and WXWIDGETS_2_8 is (unsurprisingly) the wxWidgets 2.8 branch.

I’m especially interested in feedback on whether moving definitively to GitHub would be a good thing – the main criteria for judgement being whether it makes it easier to receive contributions from others.

Categories: Haskell, wxHaskell

Who is my Community?

January 12, 2012 15 comments

“Executive” Summary

This post is more of an op-ed piece than anything technically illuminating, and certainly not Haskell specific. Those not wishing to be exposed to a high level of pontification may prefer to pass. Similarly, those unused to my writing style may be disconcerted at just how many digressions I manage to fit into a short blog post. Imagine something like Steve Yegge, but without the wit, expertise or insight, and you shouldn’t be too far off. Again, please pass if this is an issue for you.

If you are a time-starved executive type who needs to cut to the content and ignore the crap, see immediately below.

tl;dr: We built something which might be of interest to others. Should we bother to make it accessible to others when it would make our own lives, and those of our existing community, harder?

wxHaskell – why bother?

I am the lead maintainer of wxHaskell, a Haskell binding for the wxWidgets library. As part of a coming push to make wxHaskell compatible with wxWidgets 2.9, we have done quite a bit of work which has raised questions as to the ‘best’ way forward in a number of areas.

As Open Source communities go, the Haskell community is probably a relative outlier in a number of ways which matter to an Open Source Haskell contributor:

  • It is relatively small;
  • Its members have an astonishingly wide range of abilities and interests;
  • It is incredibly good natured and helpful to all, pretty much regardless of level of ability.

The small size of the community means that many projects struggle to maintain a significant level of engagement over time. There are, I think, relatively few people interested in the nuts and bolts of maintaining a GUI binding, and this effort is split across several GUI bindings (Gtk2Hs and wxHaskell being probably the best supported, but with QTHaskell and HOC). There are good reasons for the split opinion on which GUI library to bind with, and I won’t explore them here.

Why not Gtk?

One aspect of wxHaskell which I hugely appreciate is that it works on the three main platforms: Linux and other unices; Windows and OS X, and delivers ‘native’ application look and feel without too much problem on all, and this is an attribute I’m determined we should keep.

In common with many programming languages, Haskell has an FFI binding which has no problem interfacing with libraries written in C, but cannot natively interface with libraries written in C++. There are very good reasons for this (basically that there is no way for anything other than the C++ compiler which originally compiled a C++ library to know how to call or link to the functions in that library – or more technically, every C++ compiler does name mangling differently).

This is probably the reason that GTK has become the default GUI binding for many non-mainstream languages. GTK may have <opinion-not-fact>a rather ugly approach to object-orientation</opinion-not-fact>, but all of its APIs are accessible in standard C, and it is therefore relatively straightforward to write a low-level GTK binding.

GTK is an outlier in the GUI world, however. Most GUI libraries are designed in object oriented fashion, using languages, like C++, which natively support object orientation. There are also good reasons for this: GUIs are <opinion-again> one of the few application areas </opinion-again> where classical object orientation is a very good way of expressing design (the other is designing libraries for classifying shapes, which seems to be a staple of OO tutorials, but not something for which I have seen an overwhelming demand in my professional life).

Wrapping C++ for beginners

Fortunately, it is very simple to make C++ code comprehensible to systems which only understand C. Suppose you have a class something like (borrowed from C++ Language Tutorial):

class CRectangle {
    int x, y;
  public:
    void set_values(int, int);
    int get_area() const;
};

It is straightforward to write a wrapper for the class, callable in C, which looks (if you ignore error checking etc.) something like:

extern "C" {
  void CRectangle_set_values(CRectangle* self, int x, int y) {
    self->set_values(x, y);
  }
  int CRectangle_get_area(CRectangle* self) {
    return self->get_area();
  }
}

You will notice that in each case, we pass around a pointer to the C++ object and use it to call the C++ function from within a function declared (with the “C” modifier) as being compatible with C calling and linker conventions.

Most of wxHaskell is wrapper code of this type. It’s not hard to write, but there is enough of it required that the writing soon becomes tedious and error prone. SWIG is designed to do this sort of thing, but it doesn’t have a Haskell backend, and when I started to work on one I quickly discovered that the documentation is a long way out of sync with SWIG itself, although the SWIG community is very helpful on the mailing lists. I may come back to a SWIG backend one day, but life is short!

That was a pretty long digression which was aimed at saying the what we have actually built is a C binding to wxWidgets. This is therefore just as accessible to most language FFIs out there as GTK, and would form a pretty good basis for a wxWidgets binding for many non-mainstream languages.

Standing on the shoulders of giants (and the not-so-giant)

Now, all of this good stuff is built on a wide range of Open Source code. Obviously the most important component is wxWidgets itself, but wxHaskell itself was forked from wxEiffel. We also use wx-config-win, a replacement for wx-config on Windows machines, and a project called wxC was forked some time back from wxEiffel (I think) to make a generic C wrapper.

In common with many Open Source projects, unfortunately many of these are moribund – the original contributors moved on and were never replaced, and so we end up with a fork or replace question. In most cases the answer to this is easy: fork – replace is almost always too costly. However Haskell is very productive indeed, which puts the question more finely in balance, and leads me (barring a few more digressions) to what I am really pondering.

How can I best serve ‘the community’ and what is that community anyway?

As I mentioned, right now we have a fairly complete C wrapper for wxWidgets. It builds as a DLL/shared library, and uses plain C headers, which means it could be used by pretty much any language.

There’s a barrier though. In order to make it as easy as possible for the Haskell community to set up and use wxHaskell, we have used a very Haskell-centric approach to building the library (Cabal, which is the standard way of distributing Haskell source libraries).

Unfortunately, I’m sure this would be a disincentive to anyone from, say, the Ocaml, Forth, Lisp or pretty much any other language community from using or contributing to the C wrapper (“waddya mean I need to download 200MB of Haskell Platform just to build a C library?”). I certainly know that I get discouraged when looking at a potentially interesting project only to discover that I need to locate 15 different packages before I even get to try to build it – an operation which is far from guaranteed to be successful with many projects.

So now I, or rather the wxHaskell community, recognise that we have a conflict of interest: we have built something which might be useful to many other groups (but we’re not sure if there is anyone out there who cares), but making life easier for those other groups will make life harder for our existing community, at least in the short term.

However, the other side of the calculation is that if other language communities get involved in contributing to the C binding, we get a larger community with a greater level of contrbution and engagement.

So we finally come to the point: is anyone out there interested in a C binding to wxWidgets, and if so, are they interested enough to want to help?

Categories: Uncategorized

wxHaskell and wxWidgets 2.9

January 11, 2012 5 comments

Those who follow the wxHaskell developer or users lists will know that over the last few months there has been a flurry of activity on wxHaskell, spurred on by Dave Tapley. I wanted to summarise what has been happening, and what stands in the way of a release supporting wxWidgets 2.9

<blatent-plug>Dave is working on wxHaskell with the agreement of his employer, Mentics Inc. I encourage any readers who have the opportunity to place business in Mentics’ direction to do so in appreciation of this generous donation of time and talent to the Haskell community.</blatent-plug>

Why wxWidgets 2.9?

Most Linux distributions currently supply wxWidgets 2.8.x libraries from their packaging systems, and Windows has the pre-built and readily installable wxPack containing wxWidgets 2.8.12, so why are we moving to an unstable release of wxWidgets?

There are many reasons, but two stand out in particular:

  • It is the future. wxWidgets 3.0 will be released from the 2.9 line, and provides major improvements. These include:
    • Much improved Unicode support.
    • Lots of new controls: ribbon bars, wxPropertyGrid, wxWebView, wxTreeListCtrl, wxRichToolTip, wxRichMessageDialog. Together, these simplify the creation of GUIs with a ‘modern’ look and feel.
    • The stc library (used by StyledTextControl) is now part of the main library build (as is SVG, which I would like to wrap for wxHaskell in the near future).
    • Support for 64bit OS X builds, which we have been unable to support on wxHaskell due to underlying lack of support in wxWidgets.
  • We need to clean up some legacy ‘cruft’ in the code. Daan originally wrote wxHaskell for wxWidgets 2.4.x, and things have moved forward a long way since then. This is an opportunity to remove deprecated functions and offer cleaner APIs.

What is changing?

The changes listed exist today in Dave’s development repository at DarcsDen, and will be mainlined in our master repository at code.haskell.org starting the coming weekend.

  • Newly wrapped classes
    • PropertyGrid, related helper types and sample code (Dave Tapley)
    • ListView (Dave Tapley)
  • Reinstated features
    • StyledTextCtrl (Dave Tapley)
    • OpenGL (Me)
    • Ability to use wxHaskell in GHCi (Dave Tapley)
  • Build system improvements
    • Some old Eiffel legacy code in the build system has been removed. Everything is now either C++ or Haskell (Eric Kow).
    • The C wrapper for wxWidgets has been moved into a separate project, wxC and built as a shared library (Dave Tapley).
    • Work to support OS X Lion (Eric and Alessandro Vermulen).
    • A Haskell native implementation of wx-config for use on Windows platforms. We are not sure if we will use this as yet – it is experimental (Eric).
  • Bugfixes and ‘build experience’ contributions from all of the above and several others , including Shelarcy, Maciek Makowski, Henning Thielemann, Peter Simons.

What is left to do before there is a release to Hackage?

Quite a bit!

The code in Dave’s repo is reasonably well tested on Linux (Ubuntu), but has currently received insufficient love on Windows or Mac. We will need it to work reliably on all three platforms before it can be released.

I think that the main blocker right now is probably determining the correct configuration for a Windows build. We have a couple of options here. We could develop Eric’s Haskell wx-config replacement so that it has been tested for a good subset of all possible wxWidgets build configurations, or we could fix wx-config-win to the same end. This has raised some philosophical questions on ‘Open Source and community’ which I am thinking of blogging on separately.

Most of all, when we have candidate builds ready, I hope that wxHaskell users will help out by trying to install the new version on as many machines as possible, so that we can be sure that we have everything working.

Categories: wxHaskell

How does wxHaskell event handling work – part 1

June 17, 2011 1 comment

Some Background

As some of you may have noticed, there has been a minor flurry of activity (if that’s not an oxymoron) from me recently. The wxHaskell repo went offline after the attack on code.haskell.org and I decided to look at support for wxWidgets 2.9 as well as 2.8 while I was waiting for the repo to be restored.

Something I thought I had fixed (would have known better if I had been reading my mail more carefully) was a bug found by Eric when using the native Mac wxWidgets. Basically Eric was seeing an error dialog indicating an assertion failure, which it seemed to be safe to ignore, when starting an application.

The offending code, which you will find in eljevent.cpp, was:

EWXWEXPORT(wxClosure*,wxEvtHandler_GetClosure)(wxEvtHandler* evtHandler,
                                               int id,int type)
{
  wxCommandEvent  event(type,id);     //We can use any kind of event here
  wxCallback*     callback = NULL;
  bool            found    = false;

  getCallback = &callback;
  found = evtHandler->SearchDynamicEventTable( event );
  getCallback = NULL;

  if (found && callback)
    return callback->GetClosure();
  else
    return NULL;
}

I put in a workaround for the problem (green text below) in what looked like an obvious way (given that ignoring the problem seemed OK – and yes, I know that this is a horrible hack)

if (evtHandler->GetDynamicEventTable() != NULL)
 found = evtHandler->SearchDynamicEventTable( event );

Unfortunately, Eric had already discovered that ignoring the problem wasn’t really an answer

I’m afraid this causes the (surprise!) event handlers for some list boxes to stop working :-(

That’ll teach me not to take the time to understand a problem before ‘fixing’ it. OK, so Laziness and Impatience failed. Time for Hubris to take over and I tweeted that I’d look into the problem properly.

In fact, the code in wxEventHandler_GetClosure() is rather dubious in a couple of respects. The first is that the static global ‘getCallback’, is clearly a very long-standing hack (the repo source code notes this – I tend to remove comments to keep blog entries to some sort of sane length). The second is that wxWidgets doesn’t document wxEvtHandler::SearchDynamicEventTable() at all. Since wxWidgets is generally very well documented, this is a warning…

How does Event handling normally work in wxWidgets?

There are a couple of ways to do event handing in wxWidgets. The simplest, which I’m not going to explore further, is to use event macros. This won’t help us in wxHaskell as we want to define our event handlers in Haskell. The solution is to use wxEventHandler::Connect() to associate a function call with an event. This means that our supplied function will get called when the event occurs. A little plumbing will be needed to allow the called function be be written in Haskell, which we’ll look at later.

So what happens when an event occurs? The following is shamelessly cribbed from the wxWidgets documentation:

When an event is received from the windowing system, wxWidgets calls wxEvtHandler::ProcessEvent on the first event handler object belonging to the window generating the event.

The normal order of event table searching by wxEvtHandler::ProcessEvent() is as follows:

  1. If the object is disabled (via a call to wxEvtHandler::SetEvtHandlerEnabled) the function skips to step (6).
  2. If the object is a wxWindow, ProcessEvent is recursively called on the window’s wxValidator. If this returns true, the function exits.
  3. SearchEventTable is called for this event handler. If this fails, the base class table is tried, and so on until no more tables exist or an appropriate function was found, in which case the function exits.
  4. The search is applied down the entire chain of event handlers (usually the chain has a length of one). If this succeeds, the function exits.
  5. If the object is a wxWindow and the event is set to set to propagate (in the library only wxCommandEvent based events are set to propagate), ProcessEvent is recursively applied to the parent window’s event handler. If this returns true, the function exits.
  6. Finally, ProcessEvent is called on the wxApp object.

Connecting event handlers in wxHaskell

Now that the outline of event handling in wxWidgets is clear, let’s look at events in wxHaskell. I’m going to do this starting from the WXCore library as the WX library, while simpler to use, is just a Haskell wrapper around WXCore.

Widget wrappers need to provide at least two event-handler related functions: one to set the event handler and another to retrieve it. The setters and getters resemble the following (this example for Button):

buttonOnCommand :: Button a -> IO () -> IO ()
buttonOnCommand button eventHandler
  = windowOnEvent button [wxEVT_COMMAND_BUTTON_CLICKED] eventHandler (\evt -> eventHandler)

buttonGetOnCommand :: Window a -> IO (IO ())
buttonGetOnCommand button
  = unsafeWindowGetHandlerState button wxEVT_COMMAND_BUTTON_CLICKED skipCurrentEvent

The setter calls a generic event handler setter for everything deriving from Window with the control to which the handler applies, a list of the events for which the handler applies and an event handler function which will be called when the event occurs. One small point to note is that the event handler is passed twice: once as a piece of state and a second time wrapped in a lambda function.

The getter function uses an unsafe function to obtain the event handler for the requested event.

Digging further into the setters

Looking further into windowOnEvent, what do we have?

windowOnEvent :: Window a -> [EventId] -> handler -> (Event () -> IO ()) -> IO ()
windowOnEvent window eventTds state eventHandler
  = windowOnEventEx window eventIds (\ownerDelete -> return ()) -> eventHandler

Similarly, windowOnEventEx is defined as:

windowOnEventEx :: Window a -> [EventId] -> handler -> (Bool -> IO ()) -> (Event () -> IO ()) -> IO ()
windowOnEventEx window eventIds state destroy eventHandler
  = do
    let id = idAny
    evtHandlerOnEvent window id id eventIds state destroy eventHandler

In other words, we eventually transform the event handler for any given control into a generic event handler for a Window a (remember, using phantom types, wxHaskell arranges that all controls eventually resolve to a Window a), and this contains a function (strictly an IO computation) which is run when the event is triggered, and a computation which is run when the event handler is deleted. This computation takes a True argument if the owner is deleted and a False argument if we are simply disconnecting the event handler.

Digging further, we have:

evtHandlerOnEvent :: EvtHandler a -> Id -> Id -> [EventId]
                  -> handler -> OnEvent
evtHandlerOnEvent object firstId lastId eventIds state destroy eventHandler =
  do evtHandlerOnEventDisconnect object firstId lastId eventIds
     evtHandlerOnEventConnect object firstId lastId eventIds state
                              destroy eventHandler

The call to evtHandlerOnEventDisconnect simply ensures that any existing event handler is cleaned up before we install a new one. In this investigation we are more interested in:

evtHandlerOnEventConnect :: EvtHandler a -> Id -> Id -> [EventId]
                          -> state -> OnEvent
evtHandlerOnEventConnect object firstId lastId eventIds state
                         destroy eventHandler =
  do closure <- createClosure state destroy eventHandler
     withObjectPtr closure $ \pclosure ->
     mapM_ (connectEventId pclosure) eventIds
  where
    connectEventId pclosure eventId =
      evtHandlerConnect object firstId lastId eventId pclosure

We are finally getting close to where we connect up with the wxWidgets framework. The Closure a type is derived (using phantom types) from wxObject a which is itself an instance of Object a, which is basically a way of storing a C pointer.

data Object a = Object !(Ptr a)
              | Managed !(ForeignPtr (TManagedPtr a))

Now, for our purposes, we are generally using the simple case of a Ptr a Object, so looking back up at evtHandlerOnEventConnect, what we first do is to create a Closure instance:

createClosure :: state -> (Bool -> IO ()) -> (Event () -> IO ())
              -> IO (Closure ())
createClosure st destroy handler =
  do funptr  <- wrapEventHandler eventHandlerWrapper
     stptr   <- newStablePtr (Wrap st)
     closureCreate funptr (castStablePtrToPtr stptr)
   where
     eventHandlerWrapper :: Ptr fun -> Ptr () -> Ptr (TEvent ()) -> IO ()
     eventHandlerWrapper funptr stptr eventptr =
     do let event = objectFromPtr eventptr
        prev <- swapMVar currentEvent event
        if (objectIsNull event)
        then
          do isDisconnecting <- varGet disconnecting
             destroy (not isDisconnecting)
             when (stptr/=ptrNull)
               (freeStablePtr (castPtrToStablePtr stptr))
             when (funptr/=ptrNull)
               (freeHaskellFunPtr (castPtrToFunPtr funptr))
        else handler event
        swapMVar currentEvent prev
        return ()

In createClosure we wrap some state information, an event handler action and an action to perform when the event handler is disconnected. We obtain stable pointers to these (if they are not already stable pointers) and construct a closure which contains them. This construction can be safely passed to the C++ world without risk of being garbage collected in the Haskell world.

If we now go back to evtHandlerOnEventConnect, it should hopefully be clear that we are calling evtHandlerConnect with a window, a set of event IDs to handle and a closure containing the Haskell components which will be invoked when an event arrives.

Having looked at how event handlers get connected in wxHaskell, next time we’ll look at how they get called in some more detail.

Building a shared library in Cabal

November 3, 2010 2 comments

This is the second in a series about refactoring parts of wxHaskell so that it will work as expected in GHCi. There’s nothing very specific to wxHaskell here – it’s much more about implementing a custom build system in Cabal, and how to use the Cabal API.

The work described is still somewhat in progress, so there are probably a few wrinkles in the cross-platform support, but it is basically working on my development machine, so it’s slightly more than half-baked…

What we are going to do is to add a build system capable of building a shared library to the Distribution.Simple. This is not a particularly clever build system – it doesn’t do any dependency tracking, for example, which makes it somewhat painful for developers. However, the main use case for Cabal is that it is an easy way for library users to install software on their machines, and in this case dependency tracking is not really an issue as the code is only built once.

If you want real dependency tracking, use Make (or better, something like Scons – if I had a pound for every Make-based build system I’ve worked with which doesn’t quite track dependencies correctly, I’d have enough to buy a paper copy of Learn You a Haskell for Great Good).

Extending the cabal build description

In the hope of creating something reasonably re-usable (if there is enough interest, I’m happy to work with the Cabal team to get something like this into Cabal by default), I’m going to add some custom stanzas. These use the “x-something” support already built into Cabal:

x-dll-sources: a whitespace separated list of all of the C or C++ source files which need to be compiled into a shared library.

x-dll-name: the basename of the shared library we are going to create. This will expand to basename.dll on Windows, libbasename.so on most Unix systems and basename.dylib on OS X.

x-dll-extra-libraries: a list of the libraries with which the shared library must be linked at runtime. This is present to stop the library user from having to worry about this. Restrictions on some platforms mean that any libraries used here really ought to be available in a shared form on all supported platforms.

Updating the list of libraries to link

When installing a Cabal package, Cabal informs GHC of all of the libraries which need to be linked for the package to run. In this implementation, this means the shared library we are building and any libraries in x-dll-extra-libraries. These need to be added to the list of libraries which Cabal passes to ghc-pkg when the package is installed, and a good place to extract this information is in the configuration hook.

The configuration hook runs when you execute runhaskell Setup.hs configure (or as the first stage of a cabal install). You need something like the following in your Setup.hs:

main :: IO ()
main = defaultMainWithHooks simpleUserHooks { confHook  = myConfHook,
                                              buildHook = myBuildHook }

The configure hook is, rather unadventurously, named myConfHook and the build hook, which we will discuss at length later, and which is responsible for building the shared library, is myBuildHook.

Much of the effort in working with both build and configuration hooks is in finding where the information you need is stored and unwrapping and rewrapping the data structures (doubtless someone with greater Haskell-fu than me would do this in a shorter and infinitely more elegant way, but this works, so… whatever). The Cabal documentation is definitely your friend, and in this case, we start from UserHooks, which is the container for all of the hooks available in Cabal.

We are interested in creating a confHook function, and the documentation says that this has type confHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags -> IO LocalBuildInfo. It’s also worth noting, to understand what follows, that simpleUserHooks simply gives us the default set of hooks envisaged by the Cabal designers.

myConfHook (pkg0, pbi) flags = do
 lbi <- confHook simpleUserHooks (pkg0, pbi) flags
 let lpd        = localPkgDescr lbi
 let lib        = fromJust (library lpd)
 let libbi      = libBuildInfo lib
 let custom_bi  = customFieldsBI libbi
 -- Lookup DLLs to add to our extra-libs from x-dll-name and x-dll-extra-libs
 let all_dlls   = parseDLLs ["x-dll-name", "x-dll-extra-libraries"] custom_bi
 let libbi' = libbi
 { extraLibDirs = extraLibDirs libbi ++ extraLibDirs wx
 , extraLibs    = extraLibs    libbi ++ extraLibs all_dlls ++ extraLibs wx
 , ldOptions    = ldOptions    libbi ++ ldOptions    wx
 , frameworks   = frameworks   libbi ++ frameworks   wx
 , includeDirs  = includeDirs  libbi ++ includeDirs  wx
 , ccOptions = ccOptions libbi ++ ccOptions wx ++ ["-DwxcREFUSE_MEDIACTRL"]
 }
 let lib' = lib { libBuildInfo = libbi' }
 let lpd' = lpd { library = Just lib' }
 return $ lbi { localPkgDescr = lpd' }

The key lines are highlighted in red. The customFieldsBI libbi stanza returns an association list [(key :: String, value :: String)] containing all of the entries in the cabal file which start “x-”. The parseDLLs function extracts the contents of all of the stanzas whose key string matches an entry in the match list – “x-dll-name” and “x-dll-extra-libraries” in this case – and wraps them in a BuildInfo structure. We add the BuildInfo thus extracted to the extraLibs stanza.

parseDLLs :: [String] -> [(String, String)] -> BuildInfo
parseDLLs x_stanzas bi = buildBI emptyBuildInfo dlls
 where
 dlls = concat $ map (\e -> (lines . fromJust) (lookup e bi)) x_stanzas
 buildBI bi (w:ws) = buildBI (bi { extraLibs = w : extraLibs bi }) ws
 buildBI bi []     = bi

The operation of parseDLLs is straightforward: We simply update the extraLibs field of an empty BuildInfo with a complete list of the libraries provided in the selected x_stanzas. It is worth noting that it is legal to have multiple libraries in a stanza, provided that they are separated by newlines (hence the use of lines when we are constructing the list of DLLs).

After executing myConfHook, we have all of the libraries we need to link with our Haskell package (note: not the libraries we link with our DLL!), and these will be added to the list of libraries included when the package is used.

Compiling C or C++ code from within Cabal

This requires a user hook to be executed during the Cabal build phase (i.e. when you type cabal build at the command line). As I mentioned earlier, the build hook we will be executing is myBuildHook. The example shown is from an early (but working) draft. I’ll discuss why this is not quite sufficient in a later posting, but it shows the principle.

myBuildHook pkg_descr local_bld_info user_hooks bld_flags =
 do
 -- Extract custom fields customFieldsPD where field name x-cpp-dll-sources
 let lib   = fromJust (library pkg_descr)
 lib_bi    = libBuildInfo lib
 custom_bi = customFieldsBI lib_bi
 dll_name  = fromJust (lookup "x-dll-name" custom_bi)
 dll_srcs  = (lines . fromJust) (lookup "x-dll-sources" custom_bi)
 dll_libs  = (lines . fromJust) (lookup "x-dll-extra-libraries" custom_bi)
 cc_opts   = ccOptions lib_bi
 ld_opts   = ldOptions lib_bi
 inc_dirs  = includeDirs lib_bi
 lib_dirs  = extraLibDirs lib_bi
 libs      = extraLibs lib_bi
 bld_dir   = buildDir local_bld_info
 progs     = withPrograms local_bld_info
 gcc       = fromJust (lookupProgram (simpleProgram "gcc") progs)
 ver       = (pkgVersion . package) pkg_descr
 -- Compile C/C++ sources - output directory is dist/build/src/cpp
 putStrLn "Building wxc"
 objs <- mapM (compileCxx gcc cc_opts inc_dirs bld_dir) dll_srcs
 -- Link C/C++ sources as a DLL - output directory is dist/build
 putStrLn "Linking wxc"
 linkSharedLib gcc ld_opts lib_dirs (libs ++ dll_libs) objs ver bld_dir dll_name
 -- Remove C/C++ source code from the hooked build (don't change libs)
 putStrLn "Invoke default build hook"
 buildHook simpleUserHooks pkg_descr local_bld_info user_hooks bld_flags

As with the configuration hook, much of the code is simply extracting the required information from the various structures passed into the build hook. Most of the information is pretty standard for compiling any C or C++ shared library: a set of source files, the paths to search for include files, libraries to link and so on. Cabal already knows where to find compilers, linkers, archivers and the like (on Windows, GHC includes a copy of the MinGW development system, which contains a port of gcc and GNU binutils), and you can look up the full path to any of these tools using lookupProgram.

The most important lines in myBuildHook invoke the compiler and link the shared library, respectively, and are marked in red.

compileCxx :: ConfiguredProgram  -- ^ C/C++ compiler (gcc)
           -> [String]           -- ^ Compile options from Cabal and wxConfig
           -> [String]           -- ^ Include paths from Cabal and wxConfig
           -> FilePath           -- ^ Base output directory
           -> FilePath           -- ^ Path to source file
           -> IO FilePath        -- ^ Path to generated object code
compileCxx gcc opts incls out_path cxx_src =
 do
 let includes  = map ("-I" ++) incls
 out_path' = normalisePath out_path
 cxx_src'  = normalisePath cxx_src
 out_file  = out_path' </> dropFileName cxx_src </>
             replaceExtension (takeFileName cxx_src) ".o"
 out       = ["-c", cxx_src', "-o", out_file]
 opts'     = opts ++ osCompileOpts
 do_it <- True -- needsCompiling cxx_src out_file
 when do_it $ createDirectoryIfMissing True (dropFileName out_file) >> 
              runProgram verbose gcc (includes ++ opts' ++ out)
 return out_file

The CompileCxx function compiles a single C or C++ source file to object code. In the version shown there is no dependency management whatsoever, which means that every file is compiled every time cabal build is invoked. This is very inefficient for development, since there are quite a number of files to compile, and most are recompiled needlessly. A proper C++ build system would check whether the modification time of the source file or any of the files it includes is newer than the modification time of the corresponding object file. This is a lot of work to get right (most large scale C++ build systems I have encountered don’t quite get it correct!), and I have taken the judgement that since most Cabal users only compile the library the one time they install it, it is an unnecessary effort. That said, if you look in the code above, you will notice a commented out call to a function needsCompiling which checks only the modification time of the source file to compile. This is fragile (it will do the wrong thing if an include file has been changed, but not the source), but it speeds things up for development. It will not (and should not) go into released code.

I’d also like to mention the normalizePath function. This is present to fix some behaviour in System.FilePath which turns out to be, shall we say, infelicitous. The problem is an age-old one. Way back in the days of MS-DOS 1.0, which didn’t support directories, Microsoft chose to use the forward slash ‘/’ character for command line switches, which meant that it was unavailable for use as a path separator when, in about 1983, MS-DOS 2.0 came along. At that time, Microsoft chose the backslash ‘\’ character as a file separator, and software engineers across the world have been dealing with the fact that Unix chose the forward slash (some 10 years earlier) ever since.

The infelicitous behaviour comes about because of the way that MinGW (and Cygwin) try to get around this irritation. Because software originally written for Unix often hard-codes the forward slash as the directory separator, both MinGW and Cygwin treat the ‘/’ character as though it is a ‘\’ on Windows. This is OK as far as it goes, but it is fragile. In particular, many Unix derived tools generate paths with the ‘/’ separator even on Windows. When these are concatenated with paths produced using System.FilePath (or by Windows executables), you end up with paths containing both sets of separators (e.g. c:/path/to\some\windows/location.c). It turns out that some programs are not robust in the presence of both types of separators, so the only solution is to normalize all paths so that they contain only the correct path separator.

normalisePath :: FilePath -> FilePath
normalisePath = case buildOS of
 Windows -> dosifyFilePath
 _       -> unixifyFilePath

-- | Replace a character in a String with some other character
replace :: Char   -- ^ Character to replace
        -> Char   -- ^ Character with which to replace
        -> String -- ^ String in which to replace
        -> String -- ^ Transformed string
replace old new = map replace'
 where
 replace' elem = if elem == old then new else elem

unixifyFilePath = replace '\\' '/'
dosifyFilePath  = replace '/' '\\'

The code is shown above. Nothing to be especially proud of (in particular, I’m sure there must be something which does the same as replace in Data.List, but I couldn’t find it in about 30 seconds of looking!), but it works. Personally I believe that it would be better if System.FilePath did such normalization, but it’s probably a religious argument, and I’m not trying to flame anyone.

I think that’s about enough for this posting. I’ll be continuing shortly with a look at linking the shared library, and a digression into C++ static destructors. Apologies to those who value the beauty of Haskell and are offended by the ugliness of static destructors, but lifting of stones is sometimes necessary, even if what we find under them is not particularly palatable. Linking libraries isn’t much more attractive either ;-)

Categories: Haskell Tags: ,

Working around the static libstdc++ restriction

November 1, 2010 Leave a comment

I’ve been spending a bit of time, on and off, over the last few months thinking about one of the most annoying bugs in wxHaskell, and how to fix it: that wxHaskell is now unusable from within GHCi.

There are actually two separate bugs: the first, which prevents things from working at all, is that on Windows, there is no DLL version of libstdc++; the second is that you cannot restart a wxWidgets session in GHCi after the application has terminated.

The first bug is a consequence of the fact that, on Windows, GHC uses the MinGW compiler suite to do C/C++ compilation and all linking (including with Haskell objects). The version of MinGW included in GHC does not support dynamic linking of libstdc++.

When wxHaskell was fully cabalized, one of the changes was to replace a complex and fragile build system for the C++ coe in wxHaskell with a much simple cabal-based build. The consequence of this is that the C++ code and wxcore Haskell 0bject code reside in the same library, which means that wxcore needs to be linked against libstdc++.

For compiled binaries, this is not so much of a problem: the static libstdc++ is linked with the other code and all works well. However, in the GHCi case, it is fatal: GHCi doesn’t know how to statically link non-Haskell object code, although it can load DLLs. When you try to load load wxcore, GHCi complains that it cannot load stdc++ (Loading package wxcore-0.12.1.6 … <interactive>: stdc++: The specified module could not be found. can’t load .so/.DLL for stdc++ (addDLL: could not load DLL))

The second problem: inability to restart a GUI, is a consequence of wxWidgets using static destructors. These are only executed when the application fully terminates, which is not the case in a GHCi.

It turns out that a good solution to both of these problems is to repackage the C++ part of wxHaskell as a DLL. The reasons are:

  • The C++ code in the DLL needs to be linked with libstdc++, but this can be done at DLL link time. If libstdc++ is statically linked with the DLL, wxcore no longer needs to depend on linking to libstdc++ (only wxc.dll).
  • If the DLL is dynamically unloaded, the C++ static destructors will be executed (as far as I can tell, this should be true for Linux .so shared libraries as well as Windows DLLs). This means that so long as the wxc shared library is loaded at application start-up and unloaded at application termination, we should be able to correctly restart a GUI.
  • GHCi can load DLLs, so wxHaskell should become usable on GHCi again (since the libstdc++ requirement would have gone away).

The problem, and it has occupied quite a bit of my rather limited spare time, is that this requires quite a bit of re-architecture.

Changes to compile wxc as a DLL

This is probably the simplest part, since wxc was compiled as a DLL in an older version of the build system. The downside is that I would like to use cabal to do the building, since asking anyone to have prerequisites much beyond Haskell Platform greatly reduces the attractiveness of a library.

Changes to use dynamically loaded libraries.

Most usage of shared libraries is pseudo-static, that’s to say that the addresses of functions exported from the shared library are fixed-up when the application is loaded by the linker-loader. In my case, I want to do things fully dynamically. The outline code is fairly straightforward (caveat: this is outline code – I haven’t run it!).

On Windows, you need something like:

HINSTANCE dllHandle = LoadLibrary("wxc.dll");
...
if (dllHandle != NULL) {
  FnPtrType fnPtr = (FnPtrType) GetProcAddress(dllHandle, "functionName");
  if (fnPtr != NULL) {
    return fnPtr(params...);
  }
}
FreeLibrary(dllHandle);

Code in Unix-land is very similar indeed:

void* lib_handle = dlopen("path/to/libwxc.so", RTLD_LAZY);
...
if (lab_handle != NULL) {
  FnPtrType fnPtr = dlsym(lab_handle, "functionName");
  if ((error = dlerror()) == NULL) {
    return fnPtr(params...);
  }
}
...
dlclose(lib_handle);

The problem is that I have about 2,000 functions which need to be exported in this way, and the only sane approach will be to automate things. I also need to find the correct locations to load and unload libraries, and to ensure that all function pointers are invalidated when I unload the DLL.

I now have most of the work done on changes to the build system, and my ideas thought through for the dynamically loaded libraries. In the next few entries (which will hopefully be more closely spaced than of late), we’ll look at these in some more detail.

One aside: I mentioned doing some work to enable Swig to be used as a wrapper generator for the wxc wrapper layer. I still want to do this, but it has been put aside in the “too much work right now” pile. Doing things as I am planning is less maintainable in the long run, but stands a fighting chance of being finished sometime this decade, and I want people to use wxHaskell more than I need to have an awesome system for automating the generation of wrappers over C++ code.

Categories: Haskell, wxHaskell

Haskell Platform 2010.1.0.0 and wxHaskell on Windows

Edit: Please note that the issue described in this post has been fixed on newer versions of Haskell Platform, so you shouldn’t need to follow the instructions here.

Dan Haraj asked a question about installing wxHaskell on a Windows 7 machine. Since he was having problems, and since I have never verified wxHaskell installation on Windows 7, I agree to look into the problem.

Bottom line is that wxHaskell is fine on Windows 7, but there’s a problem with the Haskell Platform 2010.1.0.0 Windows installation. The issue is that the GHC version used doesn’t include C++ support, and wxHaskell needs this to build.

You have a couple of options for installing wxHaskell on Windows:

  1. Just use an older Haskell Platform installer – these work fine.
  2. Copy the required C++ support into Haskell Platform 2010.1.0.0 yourself from a MinGW installation. This is a horrible hack, as the files you will be copying to not correspond exactly to the MinGW version shipped in the Haskell Platform. They are close enough to work, however, if doing this doesn’t mortally wound your sense of software aesthetics.

You will need a copy of MinGW with gcc 3.4.5 installed, including C++ support. The automated installer at Sourceforge will install the same versions I used – just remember to tick the box marked ‘C++’ in the installer.

Once you have a suitable MinGW installation, do the following:

  • Copy cc1plus.exe from a MinGW 3.4.5 install into c:\Program Files (x86)\Haskell Platform\2010.1.0.0\mingw\libexec\gcc\mingw32\3.4.5
  • Copy libstdc++.a from a MinGW 3.4.5 install into c:\Program Files (x86)\Haskell Platform\2010.1.0.0\mingw\lib
  • Copy include\c++ directory from a MinGW 3.4.5 install into c:\Program Files (x86)\Haskell Platform\2010.1.0.0\mingw\include\c++

If you are using Windows 7, you will need to have Administrator privilege to do this.

After this, you can install wxHaskell on Windows 7 as follows:

  1. Open a cmd.exe window running as Administrator (press Start, type cmd into the search box and right click on the cmd icon to use the ‘Run as Administrator’ option.
  2. Type cabal install wx

You should have a working wxHaskell installation in a few minutes.

A couple of notes:

  • cabal install wx –user doesn’t work on Windows 7 without Administrator privilege, which kinda defeats the object. I will need to look into the reason for this, but it’s not at the top of my list.
  • You can’t run cabal install wx in an MSys shell. It breaks horribly. This means you build wxWidgets in a different shell to the one in which you install wxHaskell.

I’ve updated the wiki to reflect this…

Categories: wxHaskell Tags: ,

Wrapping optional components

In looking at this issue, I’m not really addressing my TODO list in the correct order, but Konstantin Chugalinskiy asked a question on the subject on the wxhaskell-users mailing list. His specific question was about using wxWebConnect to provide access to a WebKit browser component from wxHaskell, but the problem is more general.

An aspect of wxWidgets which has been problematic when it comes to maintenance is that there is actually a very considerable number of valid build configurations, and this continues to increase as time passes. When we moved to the Cabalized build system six months or so ago, one change which was a consequence is that we only wrap the ‘default’ subset of wxWidgets. This has the unfortunate side-effect that some features which were previously supported are no longer available. The main among these are:

  • OpenGL
  • Styled Text Control

I would prefer for these optional components to be wrapped up as separate Cabalized libraries (so if you want Styled Text Control support you would need to do something like ‘cabal install wxstc’), so started looking into the problem.

wxDirect is not a general purpose wrapper generator

The most obvious approach is to use wxdirect to generate the Haskell bindings, and this immediately causes a problem, which is this: wxdirect was really designed as a single purpose wrapper generator. It is very closely coupled to the C coding style used by the C wrappers for wxWidgets in wxHaskell, which are hand-written in a specific style. In addition, its internal design and outputs are really only intended to generate WxCore.

In the past, shelarcy made changes to wxdirect to enable it to generate Haskell bindings for the Styled Text Control, but these changes were made in a way which really supports STC only, and which still requires hand-written bindings.

Generating bindings automatically

At the moment, WxCore relies on a set of C language bindings to wxWidgets which need to be written and maintained manually. These work well, and have the considerable advantage of generally being very thin wrappers over the C++ code. The problem, however, as shown up in a couple of earlier postings to this blog, is that it is very easy for errors to creep in, and for new (or old) functions not to be wrapped.

About a year ago, on the wxhaskell-devel list there was some discussion on trying to generate the C bindings automatically. At the time, the discussions centred around using Doxygen to generate the information required to generate the C bindings. There was a proof of concept by Mads Lindstroem, using scripts written in Python to do much of the work. I have decided to see if I can make any progress in this direction (if it proves to be too much work, I’ll just make changes to wxDirect, but this will mean staying with hand-written C wrappers).

Looking at the Doxygen approach, one problem is that it seems to require a complete copy of the wxWidgets source repository (to get at the Doxygen sources) – plus I’d rather work in Haskell than Python (*) and I have never succeeded in getting *any* of the XML libraries for Haskell to work properly on my Windows development box.

(*) Nothing against Python – it is a fine language, but my spare time is very limited, and I prefer not to spend it re-learning a language I have mostly forgotten.

The most preferable solution would be to generate the wrapper code from the wxWidgets headers directly, since that would avoid transcription bugs and scale nicely to the problem of wrapping other wxWidgets components if needed. There are a couple of ways to approach this:

  • gccxml generates an XML representation of the parse tree of a piece of C++ code. It suffers from a couple of issues: I couldn’t get it to build on Windows, and I’d have to get an XML library for Haskell working on Windows. It also appears not to be very actively maintained.
  • SWIG is designed for exactly this sort of thing, but doesn’t support Haskell.

Now, SWIG support for Haskell would actually be pretty sweet, as it would make binding to many C++ libraries a great deal easier. I’m going to look into it – after all, how hard can it be? Probably the most significant issue to work through is that the WxCore API has been stable for a very long time now, so whatever approach to wrapping I take needs to come out with exactly what we have today (minus the bugs, obviously).

Categories: Haskell, wxHaskell Tags: ,

Building a text editor (Part 5)

April 6, 2010 1 comment

In this section of the tutorial, we add find/replace functionality to the editor. Much of the required support is built-in to the textCtrl, but it is only exposed by the wxCore library, so we have a rather thin wrapper over the wxWidgets C++ code to work with.

Once more, we need to work around a couple of bugs in wxHaskell – the find/replace identifiers are defined incorrectly (or missing), so we hide the imported versions and replace them later on with the correct values. We also require a couple of extra pieces of library code.

> import Graphics.UI.WXCore hiding (wxID_CUT, wxID_COPY, wxID_PASTE,
>                                   wxID_FIND, wxID_FORWARD, wxID_BACKWARD)
> import Data.Bits
> import Data.Char (toLower)
> import Data.List

We maintain a copy of the user-selected find/replace options. The user can select: the search direction; whether the operations are case sensitive; whether to look for whole word matches only and whether to wrap around from the end of the document back to the start. We wrap all of this information in a data structure.

We also need to add an extra field to our GUICtx context state. This holds a FindReplaceData (), which is an object used by many of the functions associated with the textCtrl find/replace functionality exposed in wxCore. The internal details of FindReplaceData are opaque to the Haskell wrapper, but basically it is an object which maintains state on behalf of the find/replace dialog box.

> data FRFlags = FRFlags { frfGoingDown :: Bool,
>                          frfMatchCase :: Bool,
>                          frfWholeWord :: Bool,
>                          frfWrapSearch :: Bool
>                        }
>                deriving (Eq, Show)
> data GUIContext = GUICtx { guiWin    :: Frame (),
>                            guiEditor :: TextCtrl (),
>                            guiFile   :: Var (Maybe FilePath),
>                            guiTimer  :: TimerEx (),
>                            guiPast   :: Var [String],
>                            guiFuture :: Var [String],
>                            guiSearch :: FindReplaceData ()
>                          }

As mentioned earlier, some of the identifiers in Graphics.UI.WXCore.WxcDefs are incorrect:

> wxID_FIND, wxID_FORWARD, wxID_BACKWARD, wxID_REPLACE :: Id
> wxID_FIND       = 5035
> wxID_REPLACE    = 5038  -- This one is not in WxcDefs, so not hidden earlier
> wxID_FORWARD    = 5106
> wxID_BACKWARD   = 5107

Changes to the GUI function

As usual, a few changes are needed to the top-level GUI function (step5 in this case).

To make use of the find/replace dialog box, we create a FindReplaceData instance. This will be maintained in the GUI context state structure. In this case it is created with a default indicating that forward search (wxFR_DOWN) is selected. You could also use any of the following:

  • wxFR_WHOLEWORD to indicate that search/replace should only take place on whole words.
  • wxFR_MATCHCASE to indicate that case sensitive search/replace is selected.

As well as configuring when the FindReplaceData instance is created, you can use findReplaceDataSetFlags to do the same thing. If you choose to do this, please note that according to the wxWidgets documentation, these flags can only be changed before the find/replace dialog box is first shown. Once the dialog has been shown, the values selected in the dialog are used, and changes have no effect.

> future <- varCreate []
> search <- findReplaceDataCreate wxFR_DOWN
> let guiCtx = GUICtx win editor filePath refreshTimer past future search
> set editor [on keyboard := \_ -> restartTimer guiCtx >> propagateEvent]

We also need to add menu entries and menu event handlers. Since these have been covered extensively before, no need for further comment:

> menuAppend mnuEdit wxID_PASTE "&Paste\tCtrl-v" "Paste" False
> menuAppendSeparator mnuEdit
> menuAppend mnuEdit wxID_FIND "&Find...\tCtrl-f" "Find" False
> menuAppend mnuEdit wxID_FORWARD "Find &Next\tCtrl-g" "Find Next" False
> menuAppend mnuEdit wxID_BACKWARD "Find &Previous\tCtrl-Shift-g" "Find Previous" False
> menuAppend mnuEdit wxID_REPLACE "&Replace...\tCtrl-Shift-r" "Replace" False
> evtHandlerOnMenuCommand win wxID_PASTE $ paste guiCtx
> evtHandlerOnMenuCommand win wxID_FIND $ justFind guiCtx
> evtHandlerOnMenuCommand win wxID_FORWARD $ justFindNext guiCtx
> evtHandlerOnMenuCommand win wxID_BACKWARD $ justFindPrev guiCtx
> evtHandlerOnMenuCommand win wxID_REPLACE $ findReplace guiCtx
> set win [menuBar := [mnuFile, mnuEdit]]

Dialog box functions

The dialog boxes used for both search and replace are launched by the same function – it is simply the dialog box style which changes, along with the text at the top of the dialog box. We use a couple of helper functions to make this explicit, and to keep our menu event handler functions simple:

To open a ‘find’ dialog box:

> justFind guiCtx = openFindDialog guiCtx "Find..." dialogDefaultStyle

To open a ‘replace’ dialog box, we need to modify the dialog box style to indicate that a ‘replace’ dialog box is wanted. In wxWidgets, control styles are usually represented by integers, with various stylistic elements represented by setting (or not) particular bits in the style integer.

For the built-in standard dialog boxes, there is a value dialogDefaultStyle which represents the default stylistic attributes of the control. It can be updated by setting or clearing specific flags, which we do using functions from Data.Bits. I’m probably not teaching anyone anything new here (it’s binary operations 101), but just in case…

  • To set a bit denoted by ‘flag’, use the logical ‘OR’ function (this is (.|.) in Haskell), e.g value’ = value .|. flag
  • To clear a bit denoted by ‘flag’, use the logical ‘NOT’ and logical ‘AND’ functions (complement and (.&.) in Haskell), e.g. value’ = value .&. (complement flag)
> findReplace guiCtx = openFindDialog guiCtx "Find and Replace..."
>                    $  dialogDefaultStyle .|. wxFR_REPLACEDIALOG

Let’s look at the code to handle the dialog box itself. I’ll cover this in some detail, as many of the built-in wxHaskell dialogs require similar handling.

The first line is straightforward: create the find/replace dialog instance. It is not visible by default, so we can continue to configure it without things looking strange for the user. There are a few things to notice:

  • The dialog box is associated with a parent window – in this case the Frame inside which the editor instance sits.
  • The dialog box requires an instance of FindReplaceData – we use the one we created in the top level of the GUI, and saved in the GUI context state variable.
  • The dialog style we set in either findReplace or justFind is being modified further, disabling whole word search.

The next lines are more complex. The purpose is to set event handlers for each of the events which can be handled by the find/replace dialog.

There are a few things we need to get correct for this to work. The first problem is to ensure that the GUI context is passed to the event handlers as required (it is a curried parameter they expect to receive). The second is to ensure that the events are propagated to other windows even if they are processed by the find/replace dialog event handlers.

Let’s take a look at the windowOnEvent function. This is one area where a quick look at the wxWidgets documentation will not help you much. This is because event handling in wxWidgets is hidden under an opaque macro layer which hides much of the complexity from the C++ programmer, but doesn’t help the user of a language binding like wxHaskell very much.

The signature of windowOnEvent is:

windowOnEvent :: Window a -> [EventID] -> handler -> (Event () -> IO ()) -> IO ()
windowOnEvent window events state eventHandler

The parameters are:

  • window The window to this the event handler is attached – in this case our dialog box.
  • events A list of the event IDs to which the event handler will respond. A list is required here because the event handler may respond to multiple events.
  • state Any Haskell data the programmer wishes to associate with the event handler. This is often set to be the main event handler function, since it allows the event handler to be straightforwardly retrieved (there is a function unsafeGetHandlerState to do this if required).
  • eventHandler The user-provided function to handle the event.This must be of type Event () -> IO ().

Let’s step through what happens when calling windowOnEvent:

  1. windowOnEvent window events state eventHandler is a pseudonym for  windowOnEventEx window events state (\ownerDelete -> return ()) eventHandler. In other words, we are creating an event handler where we take on the responsibility for any clean-up required when the event handler is deleted. This is the most usual case (the garbage collector normally takes care of the rest), but it you need it, there is always windowOnEventEx where you provide your own clean-up function.
  2. Disconnect any existing event handlers, calling eventHandlerOnEventDisconnect. Please note (if you are planning on  using any multi-threading) that this call modifies non thread-safe global state.
  3. Create a closure containing state, a clean-up function and a function which is called when an event occurs.
  4. For each event in events, call evtHandlerConnect (a wrapper around the wxWidgets function wxEvtHandler::Connect()). The closure created in the previous step is passed as user data.

What all of this means in practice is that a call to windowOnEvent associates an event handler and a piece of user-provided data with a particular eventID on a given window.

Let’s look at the implementation, working back from usage:

  • A call to winSet wxEVT_COMMAND_FIND findNextButton associates the event wxEVT_COMMAND_FIND with the event handler findNextButton.
  • This is equivalent to let hnd _ = findNextButton guiCtx >> propagateEvent in windowOnEvent frdialog [wxEVT_COMMAND_FIND] hnd hnd
  • Which is equivalent to windowOnEvent frdialog [wxEVT_COMMAND_FIND] (findNextButton guiCtx >> propagateEvent) (findNextButton guiCtx >> propagateEvent)

Incidentally, another, possibly slightly clearer (and more verbose) way of writing effectively the same code is:

  • windowOnEvent [wxEVT_COMMAND_FIND] findNextHdlr (\evt -> findNextHdlr)
    where findNextHdlr = findNextButton guiCtx >> propagateEvent

Finally, set the dialog box to be visible, and we’re done.

> openFindDialog :: GUIContext -> String -> Int -> IO ()
> openFindDialog guiCtx@GUICtx{guiWin = win, guiSearch = search} title dlgStyle =
>  do
>    frdialog <- findReplaceDialogCreate win search title
>                         $ dlgStyle .|. wxFR_NOWHOLEWORD
>    let winSet k f = let hnd _ = f guiCtx >> propagateEvent
>    in  windowOnEvent frdialog [k] hnd hnd
>    winSet wxEVT_COMMAND_FIND findNextButton
>    winSet  wxEVT_COMMAND_FIND_NEXT findNextButton
>    winSet  wxEVT_COMMAND_FIND_REPLACE findReplaceButton
>    winSet  wxEVT_COMMAND_FIND_REPLACE_ALL findReplaceAllButton
>    set frdialog [visible := True]

Find next / previous

The functions to find the next and previous matches are very similar, and each is a wrapper around the findNextButton function.

In the case of justFindNext we retrieve the FindReplaceData and force the search direction to be downwards. For justFindPrev we set the search direction upwards.

> justFindNext guiCtx@GUICtx{guiSearch = search} =
>  do
>    curFlags <- findReplaceDataGetFlags search
>    findReplaceDataSetFlags search $ curFlags .|. wxFR_DOWN
>    findNextButton guiCtx
> justFindPrev guiCtx@GUICtx{guiSearch = search} =
>  do
>    curFlags <- findReplaceDataGetFlags search
>    findReplaceDataSetFlags search
>                        $ curFlags .&. complement wxFR_DOWN
>    findNextButton guiCtx

To make it easier to work with the flags in a FindReplaceData, we have an auxiliary data structure FRFlags (which was defined earlier) and a function buildFRFlags to construct the data structure from the flags in a FindReplaceData. Note also that there is an additional parameter in FRFlags which is not present in FindReplaceData. This indicates whether we should wrap our searches around the text buffer, and is set by a Bool parameter to buildFRFlags.

> buildFRFlags :: Bool  -> Int  -> IO FRFlags
> buildFRFlags w x =
>   return FRFlags {frfGoingDown = (x .&. wxFR_DOWN) /= 0,
>                   frfMatchCase = (x .&. wxFR_MATCHCASE) /= 0,
>                   frfWholeWord = (x .&. wxFR_WHOLEWORD) /= 0,
>                   frfWrapSearch = w}

The findNextButton function is responsible for finding the next occurrence of the search text, taking into account all of the user preferences. It is worth noting that this function can be called both when the find/replace dialog is opened, as a result of menu selections, or as a result of hot-key combinations being pressed. This works because we retain the FindReplaceData from the last time the find/replace menu was open at all times, and pass it around as a curried parameter.

This function is pretty much a template for most of the remaining find/replace functions.

We first get the search string and flags from the FindReplaceData. The findReplaceDataGetFindString function returns the search string (s) and findReplaceDataGetFlags returns the search/replace flags. The flags are piped into buildFRFlags to obtain the flags (fs). Note that in this case we are forcing the case that the user wishes to wrap the search around the text buffer.

The findMatch function returns the location (insertion point) at which the match was found. Since it is possible that there is no match, this is wrapped up in a Maybe.

If we successfully find a match, we set the insertion point to the start of the match, then select the matched text (we can do this because we know the length of the text matched). If no match is found, we pop up a dialog box – you might prefer to do nothing in this case!

> findNextButton guiCtx@GUICtx{guiEditor = editor, guiWin = win,
>                              guiSearch= search} =
>  do
>    s  <- findReplaceDataGetFindString search
>    fs <- findReplaceDataGetFlags search >>= buildFRFlags True
>    mip <- findMatch s fs editor
>    case mip of
>        Nothing -> infoDialog win "Find Results" $ s ++ " not found."
>        Just ip -> do
>                     textCtrlSetInsertionPoint editor ip
>                     textCtrlSetSelection editor ip (length s + ip)

The findReplaceButton function is similar in many respects to findNextButton, but now we need to worry about updating the GUI history.

> findReplaceButton guiCtx@GUICtx{guiEditor = editor, guiWin = win,
>                                 guiSearch = search} =
>  do
>    s <- findReplaceDataGetFindString search
>    r <- findReplaceDataGetReplaceString search
>    fs <- findReplaceDataGetFlags search >>= buildFRFlags True
>    mip <- findMatch s fs editor
>    case mip of
>        Nothing -> infoDialog win "Find Results" $ s ++ " not found."
>        Just ip -> do
>                     textCtrlReplace editor ip (length s + ip) r
>                     textCtrlSetInsertionPoint editor ip
>                     textCtrlSetSelection editor ip (length r + ip)
>                     updatePast guiCtx

The findReplaceAllButton function is a further development of findReplaceButton. One of the key changes is that we no longer wrap our searches, as this carries a risk of infinite loops. Instead, we explicitly set the insert point (using textCtrlSetInsertionPoint) to the start of the text before doing the replacement. This is essentially equivalent to performing a wrapped replace all.

The main work of replacing all instances falls to the auxiliary function replaceAllIn. This is the same piece of code as used to do text replacement in findReplaceButton except that it is called recursively until there are no further matches

> findReplaceAllButton guiCtx@GUICtx{guiEditor = editor,
>                                    guiSearch = search} =
>  do
>    s <- findReplaceDataGetFindString search
>    r <- findReplaceDataGetReplaceString search
>    fs <- findReplaceDataGetFlags search >>= buildFRFlags False
>    textCtrlSetInsertionPoint editor 0
>    replaceAllIn s r fs editor
>    updatePast guiCtx
>      where
>        replaceAllIn s r fs editor =
>         do
>           mip <- findMatch s fs editor
>           case mip of
>               Nothing -> return () -- we're done here
>               Just ip -> do
>                            textCtrlReplace editor ip (length s + ip) r
>                            textCtrlSetInsertionPoint editor $ length r + ip
>                            replaceAllIn s r fs editor

Matching text in a TextCtrl

The findMatch function looks for text which matches the search criteria, and returns the position of the first match or Nothing.

A TextCtrl models its contents as a string, and represents the insertion point as zero-indexed offset from the start of the string. It is worth noting that some criticism could be made of the memory usage of the implementation below – this would be valid for extremely large files (we are taking a copy of the text control contents when we call textCtrlGetInsertionPoint), but the wxWidgets text control is really only useful for relatively small files – up to around 64kB (this is a hard limit on some platforms). If you are interested in working on very large files, you will probably want to implement a custom control. In any case, the approach shown is quite reasonable for demonstration purposes, and for any reasonable use of TextCtrl!

If we are doing case-insensitive search, the simplest thing to do is to transform the search and replace strings to lower case. Note that this does not affect the text in the control (we are working on a copy) or the actual text we use to replace matches (we are also working with a copy!).

We use separate functions for searching forwards (nextMatch) and backwards (prevMatch). These each return both the position of the match in the string (which is the same as the insertion point in the text control) and an indication of whether they needed to wrap around to get a match (this will disallow a match if wrap was disabled).

> findMatch query flags editor =
>  do
>   txt <- get editor text
>   ip <- textCtrlGetInsertionPoint editor
>   let (substring, string) = if frfMatchCase flags
>                             then (query, txt)
>                             else (map toLower query, map toLower txt)
>   funct = if frfGoingDown flags
>           then nextMatch (ip + 1)
>           else prevMatch ip
>   (mip, wrapped) = funct substring string
>   return $ if (not $ frfWrapSearch flags) && wrapped
>            then Nothing
>            else mip

The prevMatch and nextMatch functions are pretty similar.

The base case for prevMatch is that you are looking for an empty string. The most logical thing to do when looking for nothing is to find nothing, so:

> prevMatch _ [] _ = (Nothing, True)

The first condition in prevMatch for other cases is a check that we have not wrapped around from start of string. If we have (or would during the search) then we restart the search from then end of the string.

Otherwise, we use the nextMatch function to find our matches. To use nextMatch when we are supposed to be going backwards requires us to reverse both the substring and the string being searched, as well as changing the insert point to reflect position from the end of the string, rather than the start.

> prevMatch from substring string
>     | length string < from || from <= 0 =
>           prevMatch (length string) substring string
>     | otherwise =
>     case nextMatch (fromBack from)
>                    (reverse substring) (reverse string) of
>         (Nothing, wrapped) ->
>             (Nothing, wrapped)
>         (Just ri, wrapped) ->
>             (Just $ fromBack (ri + length substring), wrapped)
>       where
>         fromBack x = length string - x

The base case for nextMatch is exactly the same as for prevMatch, and for the same reasons.

> nextMatch _ [] _ = (Nothing, True)

The first condition covers the case where the substring is longer than the string. No search can every succeed in this case, and the search would wrap.

The second condition covers the case where searching would cause wrap-around, and we restart from the beginning of the string.

The third (normal) case works as follows:

  • Drop the characters before the (current) insertion point. If we find a match here, then we successfully matched without wrapping around the text.
  • Take all of the characters before the insertion point and further characters up to the length of the substring. If we find a match hers, then we successfully matched, but needed to wrap around to do so.
> nextMatch from substring string
>     | length substring > length string = (Nothing, True)
>     | length string <= from = nextMatch 0 substring string
>     | otherwise =
>   let after = drop from string
>       before = take (from + length substring) string
>       aIndex = indexOf substring after
>       bIndex = indexOf substring before
>   in case aIndex of
>          Just ai -> (Just $ from + ai,  False)
>          Nothing -> case bIndex of
>                         Nothing -> (Nothing, True)
>                         Just bi -> (Just bi, True)

The indexOf function finds the location of a given substring in a string.

> indexOf substring string = findIndex (isPrefixOf substring) $ tails string
Categories: wxHaskell Tags:

Building a text editor (Part 4)

In this very short post we add copy/cut/paste support to the editor. These will be accessible from the menu as well as by using the usual CUA keystrokes (Ctrl-C, Ctrl-X, Ctrl-V).

Most of the required functionality is provided by the TextCtrl itself, so the code mainly consists of connecting up event handlers to bind to the TextCtrl functions.

The first issue we need to deal with is a wxHaskell bug – this will be fixed shortly, but in the meantime it is worth knowing about. The problem is that wxHaskellprovides the wrong constants for wxID_CUT, wxID_COPY and wxID_PASTE, which are the standard identifiers used by the text control.

As always, changed text id highlighted in red.

 > import Graphics.UI.WXCore hiding (wxID_CUT, wxID_COPY, wxID_PASTE)

We can now put the correct values in ourselves. These are ‘magic’ numbers, and you’ll have to trust me that they are correct – it is not particularly easy to work out the correct values without wading through lots of C++ code.

 > wxID_MYUNDO, wxID_MYREDO, wxID_CUT, wxID_COPY, wxID_PASTE :: Id
 > wxID_MYUNDO = 5107
 > wxID_MYREDO = 5108
 > wxID_CUT    = 5031
 > wxID_COPY   = 5032
 > wxID_PASTE  = 5033

We now add the menu items and their event handlers. Nothing new here: we’ve looked at menus before. These go into the GUI top level function, along with the other UI component definitions.

 > menuAppendSeparator mnuEdit
 > menuAppend mnuEdit wxID_CUT "C&ut\tCtrl-x" "Cut" False
 > menuAppend mnuEdit wxID_COPY "&Copy\tCtrl-c" "Copy" False
 > menuAppend mnuEdit wxID_PASTE "&Paste\tCtrl-v" "Paste" False

 > evtHandlerOnMenuCommand win wxID_CUT $ cut guiCtx
 > evtHandlerOnMenuCommand win wxID_COPY $ copy guiCtx
 > evtHandlerOnMenuCommand win wxID_PASTE $ paste guiCtx

We also require three new event handler functions:

– We just copy the selected text

The copy function simply uses the textCtrlCopy function provided by the control. Remember (see part 2) that we use GUICtx as a way to pass around the global state (the GUI widgets).

 > copy GUICtx{guiEditor = editor} =
 >   textCtrlCopy editor

The cut function also uses the standard editor functionality, but in this case we are modifying the contents of the TextCtrl and we must update the GUI with an undo action.

 > cut guiCtx@GUICtx{guiEditor = editor} =
 >   textCtrlCut editor >> updatePast guiCtx

The paste function also modifies the undo history.

 > paste guiCtx@GUICtx{guiEditor = editor} =
 >   textCtrlPaste editor >> updatePast guiCtx
Categories: wxHaskell Tags:
Follow

Get every new post delivered to your Inbox.